تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Current Mix API overview0:00

Today I'd like to go over some of the thought process behind a particular real-world refactor that I'm working on for the Laravel Mix project. And it relates to the plugin system as a whole. So take a look at this. Come along for the ride. I don't expect you to make sense of all of these files. Just come along for the ride, and I'll explain as much as I can. So as you know, with Laravel Mix, you can say, well, require that in. And then you can say, well, I want this source file to be bundled up to this destination path. Maybe you want Sass as part of that. Maybe you want browser sync reloading. You understand this API probably. All right. So at the moment, that API, that public API we have, is actually stored here as a big class. So you can see things like this. JS, well, that would correspond to mix.js. All of these get registered. So you can do mix.react.

that public API we have, is actually stored here as a big class. So you can see things like this. JS, well, that would correspond to mix.js. All of these get registered. So you can do mix.react. This is, if you ever want a quick way to see what is available when you use Laravel Mix, this is the file you would go to, mix.preact, mix.typescript. We have an alias there, sass. Stand alone, you can get the basic idea. A number of things here. Let's go through one path, though. Let's go to browserSync. So this allows for automatic browser reloading. So we do a couple of things. If you request browserSync, well, we need to make sure that a few dependencies are enabled. So we pull those in if they aren't. Next, we update the master configuration, and we return the object. Okay. So we can think of that. How would we think of it? It's sort of like the registration process. This is how browserSync is registered

BrowserSync integration flow1:20

the master configuration, and we return the object. Okay. So we can think of that. How would we think of it? It's sort of like the registration process. This is how browserSync is registered with Mix. Okay. But that's not the only place, of course. There's other things. So with Webpack, we have this concept of an entry. We have rules that you can declare, and we have plugins that you can attach. So if we take a look at plugins in this case, this is the confusing master file that just takes care of all of the confusing Webpack plugin stuff that you would normally have to research on your own, but we do it for you. Now, you'll see if you want browserSync support, if I scroll down, here's what I want you to pay attention to. So if Mix is using browserSync, then only on that condition do we pull in the browserSync Webpack plugin, and then we instantiate that plugin, we pass it to Webpack, and then we send through all of the necessary

sync, then only on that condition do we pull in the browser-sync Webpack plugin, and then we instantiate that plugin, we pass it to Webpack, and then we send through all of the necessary default configuration while overriding any config that you have provided. Okay. So this is just a relatively simple example. And in some situations, you even have rules as well. And think of these rules as ways of saying, okay, well, when you come across a file of this type, that ends in, in this case, .js or .jsx, in that case, I want to kind of pipe it through this particular loader that will transform it in some way. So you can have situations, as we see here, where we have a little component, in this case, it would be browser-sync, where it has registration, it has dependencies that we need to pull in on demand, it has Webpack plugins that we need to register. And in this case, it doesn't, but in many other situations, they would have.

Motivation for cohesive components2:49

has registration, it has dependencies that we need to pull in on demand, it has Webpack plugins that we need to register. And in this case, it doesn't, but in many other situations, they would have companion rules that we need to define. So if you think about the structure for this, it can get fairly tricky, because you have this one concept of, I want browserSync support. It's almost like a little widget that I want to pop in. However, that functionality for handling the browserSync integration is split up between a number of files, and it gets a little confusing. So I've been thinking about, over the last few weeks, how to structure this into kind of a more cohesive plugin system that I can use, and then anyone else, if they want to create their own mix plugins, can hook into that exact same system. So let me show you what I've done, and then we'll do an additional one together, so you can see the workflow. So let's clear that out, and check out

Introducing components directory3:36

mix plugins, can hook into that exact same system. So let me show you what I've done, and then we'll do an additional one together, so you can see the workflow. So let's clear that out, and check out a branch I have here called components. Okay, close all of this out. So now you'll see a components directory here, and in this case, we only have two at the moment. I'm building it up. A preprocessor would sort of be like an abstract class. But let's take a look. So we said earlier that you can have these concepts where the functionality and the necessity to hook into Webpack and mix is split up between four or five different files, and it gets very confusing. So one thing to think about in this case is, can you wrap all of that up into a single class or a single module, just to make it more of a cohesive thing that you can pass around? So if that were the case, well, maybe you just have a class called Less. In that case, less compilation represents one component to mix. Then we can define

a cohesive thing that you can pass around? So if that were the case, well, maybe you just have a class called Less. In that case, less compilation represents one component to mix. Then we can define the API for this. So what would any component need to be able to declare? Let's just think about it together in a scratch file. So you have your component here. Well, you need a place to declare what dependencies are necessary. Those are dependencies that need to be pulled in on demand. So maybe you can have a method here where you return an array of dependencies that you need. Those would be npm packages you pull in. You would also need a way to register, and we talked about that earlier within this API class. You need some way to kind of bootstrap or register the component with mix. You would probably need some place to declare, well, what are the Webpack plugins that need to be hooked in? You'd also probably need Webpack rules. What are the rules that we need?

Refactoring BrowserSync component5:44

you have stylus and less and sass, and the way we would register them is fairly similar. So in that case, it makes sense to extract a parent, kind of like an abstract class, if you come from the PHP world. So here is the actual functionality that scours the paths you give us. We normalize them. We update the config and things like that. It's not important. The key to understand, though, is now we have a nice little component for less. We have one for stylus. Exact same thing. Here's the dependencies for stylus. Here's how we register it. I think it's a fairly natural way to go about this. So now let's work on that browser sync example. So we'll have components browser sync.js. We'll define that. And now we can just pluck what we need. So if we come back here, well, here's what we had originally, but we kind of want to get rid of that. So I'm going to grab that, switch over, and that's now part of our registration. And I'll paste that in. Next,

well, here's what we had originally, but we kind of want to get rid of that. So I'm going to grab that, switch over, and that's now part of our registration. And I'll paste that in. Next, though, this section here, this is what we were doing before to verify the dependencies. It ensures that these dependencies are, in fact, available. But we decided, you know what, it would be clearer if each component can just list what its dependencies are, and then we basically send the burden of taking care of that up the chain. So now whoever calls this class can check, do you have any dependencies I need? Okay, I will take care of that. So that means here we'll take this guy and return it. Now I can get rid of that entirely. Next, I set in that webpack plugins file. And again, this is where we kind of hook into webpack. This is where we extend the functionality of webpack. So let's go to browserSync.

Next, I set in that Webpack plugins file. And again, this is where we kind of hook into Webpack. This is where we extend the functionality of Webpack. So let's go to browserSync. Here's what we have currently. So now take a look. I can clean up a lot of this file. How many lines are that? 30? 33 lines? I can get rid of that entirely and move it over to this class that is responsible for that component as a whole. So now I could say Webpack plugins here. And we'll give that a reformat. But now we do have to make a couple changes. So you'll see in this file, in each case, we are pushing to this global plugins array. And then that plugins array is then passed to Webpack at the very end. But in our case now, we're not pushing to an array. We instead just want to return whatever Webpack plugins we need. So that means we should be able to return an object, in which case we just push that onto the array. Or if we return an array.

We instead just want to return whatever Webpack plugins we need. So that means we should be able to return an object, in which case we just push that onto the array. Or if we return an array of Webpack plugins that we happen to need, then we would use something like concat. So let's see. Let's return that and then clean this up and reformat. There we go. So now I'm happy because this Webpack plugins file is 30 lines less. And even better, we're able to take the pieces of this browserSync component. Before they were spread out among five different files. Now it's all contained within this one cohesive file. And what I like about this is it's actually fairly natural. If you think about it, if you were to say, I want to add blank to Webpack or I want to add this functionality, TypeScript, to Laravel Mix. Well, now I can just say, OK, well, you would need a file to declare what the dependencies are.

I want to add blank to Webpack or I want to add this functionality, TypeScript, to Laravel Mix. Well, now I can just say, OK, well, you would need a file to declare what the dependencies are. You need to register it with us. You need to be able to tell us what Webpack plugins we need, what Webpack rules, and then we'll take it from there. Next, an additional perk is that we can begin cleaning things up a bit more. So, for example, here, this can look a little tricky, a little scary, and it's not. It's just basic browser-sync configuration that we need to pass. But this stuff, you know, once it gets too long, it gets a little confusing. What is this? Is that related to Object.assign? Is this related to Object.assign? In this case, no, it's not. But it's not overly clear to see that because we have so much config. So with that in mind, I think I'd like to create a config method where we can just return whatever

Adding Sass via tests11:03

there. We were able to clean up the Webpack plugins file because it no longer has to be defined there. And it even grows, because if there were rules that we needed to declare, we would be able to strip them out of this file as well. So let's try one more together. Let's see. We do have some tests. Let's see. I haven't yet done Sass. So I do have this feature test for compiling JavaScript and Sass. So let's just set only there, because I only want to test that single file. And we're just saying, if we try to compile all of this down, then the necessary file should exist, and the manifest should appear in that way. So let's go ahead and try this out. Okay, and it does fail. Component is not a constructor. What's the issue there? It sounds like... are we exporting these? Less. Pre-processing. Ah, we forgot to export that. Okay. So it was trying to instantiate a class, and it got null. It didn't get anything in return.

like... are we exporting these? Less. Pre-processing. Ah, we forgot to export that. Okay. So it was trying to instantiate a class, and it got null. It didn't get anything in return. All right. But anyways, if we give that a run, yeah, I expect this to fail. Okay. So here's the issue. Right now, we're trying to use Sass, but here we're referencing this preProcess method, and that's actually been extracted out. You'll remember that earlier, as part of this refactor, yeah, the preProcess was moved into this dedicated class, and it was moved from the API class. So of course, now we're trying to reference a method that doesn't exist, and it's blowing up. Okay. So let's get this to green. Here's what I'll do. I'm going to create a new one for Sass. And like I said, it's similar to Less than Stylus, so I should be able to reuse a lot of this. Sass. Now, in this case, the Sass component won't have any dependencies that we have to load on

So no longer is this functionality distributed across a rules file, a plugins file, an API file, stuff like that. All right. What else? I think it's fine, honestly. If you want, you could have a pluginOptions method like we did before. So we could just take that, paste it in there, return it, and reformat and save. And then what else? Well, here you can see we're taking some defaults. We're overriding those defaults with whatever you provide. But then we're doing a second layer. We want to make sure sourceMaps are turned on no matter what. So even if you try to turn them off, we need them for the compilation. So that takes precedence. So let's make sure we accept your pluginOptions, and then we can just defer here. Yeah. And reformat. Finally, down here, we are exporting the Sass component. All right. Let's give that another run. Has it changed? No, it hasn't, but for a specific reason. We've created

Yeah. And reformat. Finally, down here, we are exporting the SAS component. All right. Let's give that another run. Has it changed? No, it hasn't, but for a specific reason. We've created this file, but there's nothing magical that automatically makes it available to mix. We actually have to declare it. And at the moment, I'm doing it in this plugin factory. So I'm storing it as an array. Maybe later, we'll just dynamically load all of the files within a directory or something like that. But until then, I'm explicitly declaring them. So we'll have our SAS file. And if you're curious, just very quickly, we map over those files. For each one, we require it. We instantiate it. If there are dependencies, then we pull them in, and then we register it. Okay. So anyways, let's give that a run. And do we get green? Yes, we do. So that has been a good refactor. But it's not just that. If we go to rules,

and then we register it. Okay. So anyways, let's give that a run. And do we get green? Yes, we do. So that has been a good refactor. But it's not just that. If we go to rules, there are rules, of course, for how we intercept the loading of a .sass file. So we can see things like this, recognize SASS imports. So here we're saying, look for any file extension. Think of this as your way of saying to Webpack, the files that I'm interested in intercepting, because I want to add my own layer of scouring, so to speak. Look for any file that ends in .sass or .scss. Then exclude some of these. It's kind of a tricky little thing we have to deal with. Don't worry about it. And then once you find one of those files, I want you to take its contents and we're going to filter it through these three loaders. So send it through the SASS loader, then through the CSS loader, and then finally to the style loader. Okay. So I'm going to take that,

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟