根据vue-loader原理实现了基础版本
vue3版本能解析.vue文件 my-vue-loader源码
vue2版本 my-vue-loader源码,在template解析时有不一样,手动添加export
学习视频 vue-loader
vue-loader原理过程
1、整体过程是,第一次进入vue-loader中,第二次进入VueLoaderPlugin中,第三次再进入vue-loader中,最终得到编译完成代码
2、第一次进入vue-loader是因为,VueLoaderPlugin 初始化 apply方法中,在所有loader前添加一个 带 pitch 方法的loader叫pitcher
3、pitcher的pitch方法因为是第一个loader,pitch方法最先执行,resourceQuery方法判断,当前引入文件是否带有vue参数,发现没有vue参数,那么就跳过pitch,继续执行,就进入了vue-loader,这就是第一次执行
import App from './App.vue'
我们代码中这样引入Vue文件,就会第一次进入 vue-loader方法中
4、上面这句代码,经过vue-loader后返回如下代码
import scrpt from './App.vue?vue&type=script&id=data-v-yug6h445&lang=js'
import { render } from './App.vue?vue&type=template&id=data-v-yug6h445&lang=js'
import './App.vue?vue&type=style&id=data-v-yug6h445&index=0&lang=style'
import './App.vue?vue&type=style&id=data-v-yug6h445&index=1&lang=style'
script.render = render
script.__scopeId = 'data-v-yug6h445'
export default script
5、上述代码就是第一次进入vue-loader后返回的代码,webpack识别到代码里新增了三个import,又执行代码解析,此时就会进入VueLoaderPlugin的pitch方法中,因为在pitch方法中有判断路径参数中是否有App.vue?vue,因为第一次没有vue参数,所以就直接跳过了pitch方法,第二次就进入了pitch方法
6、pitch方法的目的是用内联方式把template,script,style需要用到的解析loader都写在import语句里面,所以pitch方法后,返回的代码变为了
import scrpt from '!!babel-loader!vue-loader!./App.vue?vue&type=script&id=data-v-yug6h445&lang=js'
import { render } from '!!vue-loader!./App.vue?vue&type=template&id=data-v-yug6h445&lang=js'
import '!!style-loader!css-loader!./App.vue?vue&type=style&id=data-v-yug6h445&index=0&lang=style'
import '!!style-loader!css-loader!./App.vue?vue&type=style&id=data-v-yug6h445&index=1&lang=style'
script.render = render
script.__scopeId = 'data-v-yug6h445'
export default script
7、但是怎么知道每种类型文件由哪些loader执行呢?答案就是VueLoaderPlugin中,里面会改写在webpack.config.js中配置的rules对象,一是在rules第一个增加pitcher的loader,二是克隆原有rules并添加进入,但是cloneRules会删除test判断,增加resource和resourceQuery,其中把每个文件的后缀比如
import scrpt from './App.vue?vue&type=script&id=data-v-yug6h445&lang=js'改为./App.vue.js,取得文件路径和lang字段拼接,这样第二步中pitch就就可以得到匹配到的loader集合了
8、第二步pitch中还需要增加./stylePostLoader.js,这个loader会放到 style-loader, css-loader , ./stylePostLoader.js,这样来处理css文件,因为stylePostLoader中调用 @vue/compiler-sfc编译style样式,增加scoped属性选择器
9、现在我们得到了如下的代码import
import scrpt from '!!babel-loader!vue-loader!./App.vue?vue&type=script&id=data-v-yug6h445&lang=js'
现在到了第三步,再次进入vue-loader,这里是根据是否有参数判断是第一次还是第二次进入,第二次进入需要解析出每个template, script, style 块的具体代码返回了,但是也有不同。
需要不同调用compileTemplate,compileScript,style则是根据index返回不同style块
const VueCompiler = require('@vue/compiler-sfc')
const params = getUrlParams(loaderContext.resourceQuery)
const id = params.get('id')
switch (params.get('type')) {
case 'template':
const { code } = VueCompiler.compileTemplate({
source: descriptor.template.content,
id: id
})
return loaderContext.callback(null, code)
case 'script':
const script = VueCompiler.compileScript(descriptor, { id: id })
return loaderContext.callback(null, script.content)
case 'style':
const style = descriptor.styles[params.get('index')].content
return loaderContext.callback(null, style)
default:
return loaderContext.callback(null, sourceCode)
}
上述就是vue-loader的流程。
项目中从0配置webpack,babel,vue-loader,css解析包
"devDependencies": {
"@babel/core": "^7.21.3",
"@babel/preset-env": "^7.20.2",
"@vue/babel-plugin-jsx": "^1.1.1",
"@vue/babel-preset-jsx": "^1.4.0",
"babel-loader": "^9.1.2",
"css-loader": "^6.7.3",
"hash-sum": "^2.0.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.2",
"vue-loader": "^17.0.1",
"webpack": "^5.76.3",
"webpack-cli": "^5.0.1"
},
"dependencies": {
"vue": "^3.2.47"
}
关于 vue-loader ,需要了解到它的作用,我们普遍都是在SFC文件中写 template, script, style ,通过看源码,还有custom标签,基本使用方式如下:
1、webpack中配置 loader -> vue-loader
module: {
rules: [
{
test: /.vue$/,
// use: ['vue-loader']
loader: path.resolve(__dirname, './vue-loader/index.js')
}
]
},
2、配置插件 VueLoaderPlugin
const { VueLoaderPlugin } = require('vue-loader')
plugins: [
new VueLoaderPlugin()
]
为什么会这么麻烦,要配置loader还要配置plugin呢?后面我们说清楚之后就很明白了。
前置知识: loader 的 pitch 方法
官网引述:
loader 总是 从右到左被调用。有些情况下,loader 只关心 request 后面的 元数据(metadata) ,并且忽略前一个 loader 的结果。在实际(从右到左)执行 loader 之前,会先 从左到右 调用 loader 上的 pitch 方法。
举个例子:
我们配置的css处理器,都是如下
module:{
rules:[
{
test: /\.[s]css/,
use: ['style-loader','css-loader','postcss-loader','sass-loader']
}
]
}
我们知道,执行顺序是从右往左执行,sass-loader -> postcss-loader -> css-loader -> style-loader,这种就叫normal loader执行顺序。
而,我们知道loader的写法如下,就是导出一个默认函数,参数是源文件代码,返回值是esm或者commonjs的导出字符串,比如:
function MyLoader(source){
return `export default 'hello world!'`
}
![图片转存失败,建议将图片保存下来直接上传
那么,MyLoader这个loader就会导出hello world 这句话
但是normal loader执行顺序是从 右->左,还有一种顺序,如果loader导出上有 pitch 属性方法,那么pitch属性方法会先执行,按照左 -> 右的顺序,如果pitch有返回值,那么就中断执行后面的