vue-loader处理vue⽂件
loader:"vue-loader" ,引导vue⽂件被vue-loader/lib/index.js处理
第⼀步:解析vue⽂件
const utils = require('@vue/component-compiler-utils')
utils.parse(.vue⽂件),返回⼀个json:
{
"template": {
"type": "template",
"content": "\n<div @click=\"setName\">\n    app\n</div>\n",
"start": 10,
"attrs": {},
"end": 61
},
"script": {
"type": "script",
"content": "//\n//\n//\n//\n//\n//\n\nexport default {\n    name: \"app\",\n    methods:{\n        setName(){\n            console.log('my name');\n        }\n    }\n}\n",    "start": 82,
"attrs": {},
"end": 236
},
"styles": [
{
"type": "style",
"content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ndiv{\n    width: 300px;\n}\n",
"start": 261,
"attrs": {
"scoped": true
},
"scoped": true,
"end": 299
}
],
"customBlocks": [],
"errors": []
}
第⼆步:⽣成代码
在vue-loader/lib/index.js中有这样的⼀个代码:
let code = `
${templateImport}
${scriptImport}
${stylesCode}
/*  */
/* normalize component */
/* normalizer 函数式在chrome中执⾏,并不是在node中执⾏*/
import normalizer from ${stringifyRequest(`!${componentNormalizerPath}`)}
var component = normalizer(
script,
render,
staticRenderFns,
${hasFunctional ? `true` : `false`},
${/injectStyles/.test(stylesCode) ? `injectStyles` : `null`},
${hasScoped ? JSON.stringify(id) : `null`},
${isServer ? JSON.stringify(hash(request)) : `null`}
${isShadow ? `,true` : ``}
)
`.trim() + `\n`
code += `\nexport ports`
这个模板字符串最终形式如下:
这是⼀个模块有import 和export ,和我们⾃⼰写的代码⼀样。
import { render, staticRenderFns } from "./app.vue?vue&type=template&id=6940f262&scoped=true&"
import script from "./app.vue?vue&type=script&lang=js&"
export * from "./app.vue?vue&type=script&lang=js&"
import style0 from "./app.vue?vue&type=style&index=0&id=6940f262&scoped=true&lang=css&"
import normalizer from "!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js"
var component = normalizer(
script,
render,
staticRenderFns,
false,
null,
"6940f262",
null
)
export ports
这样的代码被webpack接收之后发现有import语句,重新调⽤vue-loader第⼆次对同⼀个vue⽂件的处理。
不过这次query上有了type类型,⼀进到vue-loader就被截胡了,直接根据type参数跳转到另外的loader处理。跳转代码如下:
if (pe) {
return selectBlock(
descriptor,
loaderContext,
incomingQuery,
!!options.appendExtension
)
}
另外loader是从哪⾥来的呢,这就要说到new VueLoaderPlugin()的⽤意了。
VueLoaderPlugin的⽤处是利⽤webpack钩⼦在旧rules上添加了其他loader。
如果type=script,最终的loader如下:
-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0
!
../../../node_modules/babel-loader/lib/index.js
!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0
!../../../node_modules/vue-loader/lib/index.js??vue-loader-options
!./topnav.vue?vue&type=script&lang=js&"
如果type=template,最终的loader如下:
-!cache-loader?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"57422ecc-vue-loader-template"} !../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options
!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0
!../../../node_modules/vue-loader/lib/index.js??vue-loader-options
!./history.vue?vue&type=template&id=f89b51d2&scoped=true&"
如果type=style,最终的loader如下:
-!../../../../node_modules/vue-style-loader/index.js??ref--6-oneOf-1-0
!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1
!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js
!../../../../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2
!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0
!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options
!./voice.vue?vue&type=style&index=0&id=b09c9dcc&scoped=true&lang=css&"
1.加⼊!前缀,不使⽤config loader中的normal loader,例如require('!a-loader!./a.j s');
2.加⼊!!前缀,不使⽤config loader中的pre loader,normal loader,post loader,例如 require('!!a-loader!./a.js');
3.加⼊-!前缀,不使⽤config loader中的normal loader,pre loader,例如require('-!a -loader!./a.js');
上⾯的loader处理之后分别会⽣成如下的代码:
utilspileTemplate处理template,返回值被 normalizer的 render, staticRenderFns接收
{
ast: {
type: 1,
tag: 'div',
attrsList: [ [Object] ],
attrsMap: { '@click': 'setName', class: 'sdsd' },
rawAttrsMap: {},
parent: undefined,
children: [ [Object], [Object] ],
plain: false,
staticClass: '"sdsd"',
hasBindings: true,
events: { click: [Object] },
static: false,
staticRoot: false
},
code: 'var render = function() {\n' +
'  var _vm = this\n' +
'  var _h = _vm.$createElement\n' +
'  var _c = _vm._self._c || _h\n' +
'  return _c("div", { staticClass: "sdsd", on: { click: _vm.setName } }, [\n' +
'    _vm._v("\\n    app\\n    "),\n' +
'    _c("img", { attrs: { src: "./a.png", alt: "" } })\n' +
'  ])\n' +
'}\n' +
'var staticRenderFns = []\n' +
'render._withStripped = true\n',
source: '\n' +
vue json字符串转数组
'<div @click="setName" class="sdsd">\n' +
'    app\n' +
'    <img src="./a.png" alt="">\n' +
'</div>\n',
tips: [],
errors: []
}
const { code } = compiled
return code + `\nexport { render, staticRenderFns }`
utilspiledStyle处理的结果如下,返回值被 normalizer的 style0接收
const { code, map, errors } = {
code: '\ndiv[data-v-12]{\n    width: 300px;\n}\np[data-v-12]{\n    background: red;\n}\n',
map: undefined,
errors: [],
rawResult: LazyResult {
stringified: true,
processed: true,
result: Result {
processor: [Processor],
messages: [],
root: [Root],
opts: [Object],
css: '\n' +
'div[data-v-12]{\n' +
'    width: 300px;\n' +
'}\n' +
'p[data-v-12]{\n' +
'    background: red;\n' +
'}\n',
map: undefined,
lastPlugin: [Function]
}
}
}
this.callback(null, code, map)
脚本部分没有添加额外的处理,直接返回了vue⽂件中的script部分,返回值被 normalizer的 script接收最后回到normalizer,这个在浏览器执⾏的⽅法会接收到如下参数:
script:就是在vue组件中export default中定义的所有内容
render:由template⽣成的render函数,在上⾯能看到
staticRenderFns :由template⽣成,上⾯能看到
false:代表不是函数组件
null: 代表是否在页⾯插⼊组件⾏内样式,此处没有使⽤sytle-loader处理,结果为null
6940f262 : 组件中样式的scopeId
如此⼀个组件就编译完了。
中间的⼀些状态可以执⾏这个⽂件

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。