vux-loader
工具的作用是对.vue
代码进行预处理,不限于 vux 组件库。
它是针对webpack+vue-loader
项目的工程化工具,简化了webpack插件和loader的使用和编写,支持在vue-loader处理之前进行预处理,同时内置对vux组件专用的配置和优化插件。
当然除了.vue文件外,你还可以对js文件进行预处理。说好的处理.vue文件,为什么连js
文件也不放过呢?因为只有处理js才能实现理想工程化。举个例子,如果用户需要在main.js
中调用vux的plugin,他需要这样做:
import AlertPlugin from 'vux/src/plugins/Alert'\nimport ToastPlugin from 'vux/src/plugins/Toast'\n
\n虽然路径不长,但是看着相当不和谐,为了简化这个操作vux提供了更简洁的写法:
\nimport { AlertPlugin, ToastPlugin } from 'vux'\n
\n这个操作即是通过js-parser
插件解析main.js里的import
语法来实现的,最终进入babel的代码和上面单独引入一致。
这个工具也许会帮你进一步打开Vue
项目工程化的思路。
\n 作为通用工具,即使你没有使用Vux,依然可以使用它来进行各种代码处理。\n
\n 目前支持 vue-loader@>9.x, 低版本没有具体测试。\n
\n `vux!vue`的loader写法只兼容webpack@1.x,为了兼容webpack@2.x,处理后的loader配置是`vux-loader!vue-loader`\n
说明一下vux-loader
是如何和vue-loader
搞基配合的,vue-loader的issue里不少同学遇到预处理.vue的问题。
如果你看过webpack
loader的介绍,理论上说如果需要自己手动先处理代码再传入vue-loader
,你只需要这样对loader做配置: vue-loader!my-loader
。但是如果你试过,就知道这个目前行不通。vue-loader
不会接收上一个loader的source进行处理,因为.vue文件的特殊性,它直接生成了loader string进入下一个处理,处理后的结果大概是这样的:
/* styles */\nrequire(\"!!vue-style-loader!css-loader!vue-loader/lib/style-rewriter?id=data-v-20d7ba9a!vue-loader/lib/selector?type=styles&index=0!./index.vue\")\n\n/* script */\n__vue_exports__ = require(\"!!babel-loader!vue-loader/lib/selector?type=script&index=0!./index.vue\")\n\n/* template */\nvar __vue_template__ = require(\"!!vue-loader/lib/template-compiler?id=data-v-20d7ba9a!vue-loader/lib/selector?type=template&index=0!./index.vue\")\n
\n那么vue-loader提供了自定义loader string的选项,直接配置可否呢,#531,直接配置会破坏原来的loader参数,导致不能Live reload。
\n那么最终能想到的就是直接修改vue-loader
生成的代码,这样既不会影响vue-loader
原来的逻辑,也能自由控制。
最终实现方式是前置vux-loader
即:vux-loader!vue-loader
,vux-loader将vue-loader生成的代码二次处理,分割loader string并(强行)插入vux的 template-loader, style-loader, script-loader,那么你就能理解vux-loader的配置方式了,在3大基本loader里获取到vux-loader的插件配置列表逐个做处理就行了。
上面的代码强行处理后是这样的:
\n/* styles */\nrequire(\"!!vue-style-loader!css-loader!vue-loader/lib/style-rewriter?id=data-v-20d7ba9a!./../../../../vux-loader/src/style-loader.js!vue-loader/lib/selector?type=styles&index=0!./index.vue\")\n\n/* script */\n__vue_exports__ = require(\"!!babel-loader!vue-loader/lib/selector?type=script&index=0!./../../../../vux-loader/src/script-loader.js!./index.vue\")\n\n/* template */\nvar __vue_template__ = require(\"!!vue-loader/lib/template-compiler?id=data-v-20d7ba9a!./../../../../vux-loader/src/template-loader.js!vue-loader/lib/selector?type=template&index=0!./index.vue\")\n
\n原理就是这么简单(但是处理时还是遇到一些问题),为了方便使用,编写了常用的一些插件。
\n至于js-parser
就只是在babel-loader
前加上了vux-loader
的js-loader
。
而所有的loader, plugins设置都是在vux-loader
的merge
方法里来完成。
我们可以使用vux-loader
做一些很有意思的事。
\n比如 #542 提到的根据当前feature为判断要输出哪一部分代码,现在已经直接支持,请看 template-feature-switch
部分。
对于公用组件而言,完全可以实现构建时进行组件瘦身,只保留使用到的代码。
\n当然如果你愿意,你完全可以自己实现一门语言。
\nnpm install vux-loader --save-dev\n
\n为了减少使用成本,只需要调用merge
方法对原来webpack
配置进行操作:
const webpackConfig = {} // 原来的webpack配置\n\nconst vuxLoader = require('vux-loader')\n\nmodule.exports = vuxLoader.merge(webpackConfig, {\n options: {},\n plugins: [{\n name: 'vux-ui'\n }]\n})\n
\n\n 更新配置后需要重启npm run dev命令\n
\nenv
非必选,定义当前环境变量,只在vux-loader里使用,用来判断哪些插件需要被执行(如果plugin有定义envs的话),目的是实现一份配置多个环境使用。插件为一个数组列表,根据需要可以添加你需要的插件,插件名为必须,有些组件不需要额外配置选项。
\n公用参数为:
\nname
必须
,插件名字envs
非必须
,数组,当前插件在哪些环境变量里执行,不定义则默认执行下面的插件配置代码将省略 plugins:[]
的书写。
\n\nscript代码替换处理
\n
[{\n name: 'script-parser',\n fn: function (source) {\n return source.replace('VARIABLE', 'v2')\n }\n}]\n
\n\n\nstyle代码替换处理
\n
[{\n name: 'style-parser',\n fn: function (source) {\n return source.replace('black', 'white')\n }\n}]\n
\n\n\ntemplate代码替换处理
\n
\n\n适用于对
\n<template></template>
模板代码做自定义处理
\n适用于某些更改不频繁但非服务端配置的文字,可能调用多次,也可能手动更改或者批量替换相对麻烦同样你也可以用来从接口获取最新配置替换特定的占位字符
\n当然也适用于在源码中对
\npm
进行吐槽(千万要记得production环境要有配置,否则可能会上新闻。)
template-parser
replace
方法进行替换插件配置:
\n[\n name: \"template-parser\",\n replaceList: [{\n test: /DeathToPM/g,\n replaceString: '微博-随时随地发现新鲜事'\n }, {\n test: /呵呵我们压根没有底线/g,\n replaceString: '我是有底线的'\n }],\n fn: function (templateSource) {\n return templateSource.replace('智障', '机智')\n }\n]\n
\n\n\n项目里js文件处理,在babel-loader前进行处理
\n
[{\n name: 'js-parser',\n fn: function (source) {\n return source.replace('black', 'white')\n }\n}]\n
\n实现根据变量切换template代码
\n参数:
\nfeatures
,变量列表,值只能为true或者false{\n name: 'template-feature-switch',\n features: {\n feature1: true,\n feature2: false\n }\n}\n
\n<template>\n <div>\n <on feature=\"feature1\">\n AWESOME FEATURE 1 is ON\n </on>\n <off feature=\"feature1\">\n AWESOME FEATURE 1 is OFF\n </off>\n </div>\n</template>\n
\n那么当 feature1 为 true时,将输出 AWESOME FEATURE 1 is ON
, 反之则输出 AWESOME FEATURE 1 is OFF
。注意on
标签内不限定内容,可以为任何标签代码块,但避免在on off 里面再嵌套 on off
\n vux组件的配套工具,如果没有使用vux, 不需要添加。\n
\n如果配合vux
使用,需要手动启用。默认不需要设置选项。
该插件做了以下处理:
\nbabel
编译 vux 的js源码vux-loader!vue-loader
import
组件调用解析为单组件引入{\n name: 'vux-ui'\n}\n
\n那么你就可以很方便地引入组件了:
\n// 0.x\nimport Group from 'vux/src/components/group'\nimport Cell from 'vux/src/components/cell'\n\n// 2.x\nimport { Group, Cell } from 'vux'\n
\n\n请使用正确的 yml 格式。冒号和值之间是有一个空格的,错误的格式将无法生效。\n
\non-show:\n en: emits when popup shows\n zh-CN: 弹窗显示时触发\n
\n如果你只是调用中文语言的vux组件,那么你不需要做任何配置。
\n如果你需要调用英文语言的vux组件,需要配置语言
\n{\n name: 'i18n',\n vuxStaticReplace: true,\n vuxLocale: 'en'\n}\n
\ni18n
block,并且需要动态切换语言,那么需要配置插件抽取i18n的内容,并设置非静态替换{\n name: 'i18n',\n vuxStaticReplace: false,\n staticReplace: false,\n extractToFiles: 'src/locales/components.yml',\n localeList: ['en', 'zh-CN']\n}\n
\n然后你就可以引用vuex-i18n
插件实现多语言切换了。
参考 main.js 及 vuex-i18n 文档(仅当参考,你也可以使用其他i18n插件)。
\n\n注意,path所在文件必须是简单的less变量对,不能import其他文件或者引入变量。\n
\n\n\nless 变量设置和替换
\n
\n\n适用于全局变量替换, 方便切换主题
\n
\n这意味着,你不再需要为每个页面引入全局的less文件了,你只需要设置lang为less就可以直接使用变量了
<style lang="less">\n.info {\n color: @font-size;\n}\n</style>\n
\n插件配置:
\n{\n name: 'less-theme',\n path: 'src/styles/theme.less'\n}\n
\n\n\ncss 重复代码清除
\n
\n 建议使用vux组件的用户使用,因为vux直接引用less,最终构建的css文件确实会有冗余。\n
\n\n\n在webpack 构建完成后对生成的css文件使用cssnano进行重复样式清除。建议只在production环境下开启,因为dev环境没有必要。
\n
{\n name: 'duplicate-style',\n events: {\n done: function () {\n console.log('done!')\n }\n }\n}\n
\n\n\nhtml文件处理事件回调
\n
\n\n适用于在写入html(一般为index.html)文件前进行内容修改,比如替换特定内容
\n
\n比如写入js配置变量,改变cdn域名,将manifest文件直接写入html以减少请求等
{\n name: 'html-build-callback',\n events: {\n 'after-html-processing': function (data, cb) {\n data.html += 'magic footer'\n cb(null, data)\n }\n }\n}\n
\n可用事件请参考 html-webpack-plugin
\n\n\n构建完成事件回调
\n
\n本质上是在webpack plugin的 done 事件后触发
{\n name: 'build-done-callback',\n fn: function () {\n console.log('done!')\n }\n}\n
\n如果你翻看vue-loader
的issue, 就会发现不少同学在说,为什么sass, less 变量不能全局使用,需要在每个.vue组件里引入,这个很重复,有没有办法解决。目前除了在webpack里定义路径alias
外没有其他方法。但是有了vux-loader
,你可以用style-parser
在每个.vue
文件的style前面加上变量的引入了,只要一句代码。
{\n name: 'style-parser',\n fn: function (source) {\n return "@import '../styles/variable.less'\\n" + source // 你可以根据this.resourcePath 来确定是否要引入以及引入的相对路径\n }\n}\n
\nvux
的组件有几十个,同样demo也有几十个,因为webpack并不支持require变量,那么在main里中实现每个组件异步加载都需要这样:
const routes = [\n {\n path: '/component/actionsheet',\n component: function (resolve) {\n require(['./demos/Actionsheet'], resove)\n }\n }\n]\n
\n作为懒人,写几十次这样的代码是一件比较烦人的事,作为热爱地球的人,这样也很不环保,但是有了vux-loader
, 我们可以这样:
const routes = []\n
\n然后在js-parser
里获取列表数组直接替换, 并且可以调用webpack的this.addDependency
添加依赖实现修改列表时自动reload
。从此添加删除组件只要加上或者删除名字就可以了,真是懒机智。
{\n name: 'js-parser',\n test: /main\\.js/,\n fn: function (source) {\n this.addDependency(demoPath)\n let list = fs.readFileSync(demoPath, 'utf-8')\n list = JSON.parse(list)\n let str = []\n list.forEach(one => {\n str.push(`{\npath: '/component/${toDash(one)}',\ncomponent: function (resolve) {\nrequire(['./demos/${one}.vue'], resolve)\n}\n}`)\n })\n\n str = `[${str.join(',\\n')}]`\n source = source.replace('const routes = []', 'const routes = ' + str)\n return source\n }\n}\n
\nvux是没有main入口文件的,因此需要把import语句转换成单个组件引入。但是因为对语法树分析比较烦,能用正则替换的当然就是用正则替换,简单粗暴实用。具体可以看源码。
\n如果以js方式分发,一般默认一个语言,用户可以加上另外的语言包,但是代码里必不可少存在相应的转换函数,对于只需要单语言的人来是个浪费和繁琐。要么所有一起打包,这个也极浪费。要么一个一个打包,这样又失去了动态多语言支持。
\n于是vux
在vux-loader
的支持下实现了使用时构建
,无论是静态输出和动态支持。
请到https://github.com/airyland/vux-loader/issues
\n