|
1 |
| -# theme-variables-loader |
| 1 | +# vue-style-variables-loader |
| 2 | + |
| 3 | +这个基于 webpack 的插件试图解决使用 Vue 开发中的两个问题: |
| 4 | +1. 在 Vue 单文件中自动引入变量文件 |
| 5 | +2. 选用了一个 UI 框架并使用了框架提供的主题解决方案,在组件中想使用这些主题变量,但又不想使用框架指定的预处理器 |
| 6 | + |
| 7 | +## 问题1:在 Vue 单文件中引入变量文件 |
| 8 | + |
| 9 | +通常我们的项目中包含一个定义了常用变量的文件,以开发选择的预处理器格式存在。 |
| 10 | +使用时,在每个 Vue 单文件组件的 style 块中都需要手动引入这个变量文件。虽然使用 webpack alias 之后不用考虑路径问题,但如果能自动引入将方便很多。 |
| 11 | +``` vue |
| 12 | +// Component.vue |
| 13 | +
|
| 14 | +<style lang="scss"> |
| 15 | + // 引入变量文件 |
| 16 | + @import "@/styles/variables.scss"; |
| 17 | + // 开始使用变量 |
| 18 | +</style> |
| 19 | +``` |
| 20 | + |
| 21 | +有人针对这个问题向 vue-loader 提出了[相关 issue](https://github.com/vuejs/vue-loader/issues/328)。而 vue-loader 认为这个工作应该交给各个预处理器 loader 完成。 |
| 22 | + |
| 23 | +例如使用 sass 时,可以[使用 sass-resources-loader](https://vue-loader.vuejs.org/en/configurations/pre-processors.html) |
| 24 | +``` javascript |
| 25 | +{ |
| 26 | + loader: 'sass-resources-loader', |
| 27 | + options: { |
| 28 | + resources: 'variables.scss' |
| 29 | + } |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +或者[使用 sass-loader](https://github.com/webpack-contrib/sass-loader#environment-variables)的注入环境变量功能。[keen-ui就采用了这种方式支持用户覆盖预定义的主题变量](https://github.com/JosephusPaye/Keen-UI/blob/master/Customization.md#customization) |
| 34 | +``` javascript |
| 35 | +plugins: [ |
| 36 | + new webpack.LoaderOptionsPlugin({ |
| 37 | + options: { |
| 38 | + sassLoader: { |
| 39 | + data: '@import "src/styles/variables.scss";', |
| 40 | + includePaths: 'src/styles' |
| 41 | + }, |
| 42 | + context: path.resolve(__dirname) // your project root |
| 43 | + } |
| 44 | + }) |
| 45 | +] |
| 46 | +``` |
| 47 | + |
| 48 | +而在 stylus-loader 中,可以使用[import](https://github.com/shama/stylus-loader#using-nib-with-stylus)达到引入全局变量的目的。 |
| 49 | +``` javascript |
| 50 | +plugins: [ |
| 51 | + new webpack.LoaderOptionsPlugin({ |
| 52 | + test: /\.styl$/, |
| 53 | + stylus: { |
| 54 | + use: [require('nib')()], |
| 55 | + import: ['~nib/lib/nib/index.styl'] |
| 56 | + } |
| 57 | + }) |
| 58 | +] |
| 59 | +``` |
| 60 | + |
| 61 | +less-loader 中并没有找到解决方法,似乎只能每次手动引入了。 |
| 62 | + |
| 63 | +可以看出,各个预处理器 loader 都有自己的方式解决这个问题。而且就算是手动引入,代价也并不高,让我们继续来看第二个问题。 |
| 64 | + |
| 65 | +## 问题2:选用和 UI 框架不同的预处理器开发 |
| 66 | + |
| 67 | +各个 UI 框架都有自己的主题解决方案,例如: |
| 68 | +* vuetify [使用 stylus hash 变量](https://vuetifyjs.com/style/theme)覆盖[预定义的变量列表](https://github.com/vuetifyjs/vuetify/blob/dev/src/stylus/settings/_theme.styl)。 |
| 69 | +* keen-ui [使用用户自定义的 sass 变量文件](https://github.com/JosephusPaye/Keen-UI/blob/master/Customization.md#customization)覆盖[预定义的变量列表](https://github.com/JosephusPaye/Keen-UI/blob/master/src/styles/variables.scss) |
| 70 | +* vux 通过 vux-loader [使用用户自定义的 less 变量文件](https://vux.li/#/?id=%E9%A2%9C%E8%89%B2%E9%85%8D%E7%BD%AE)覆盖[预定义的变量列表](https://github.com/airyland/vux/blob/v2/src/styles/variable.less) |
| 71 | +* [vue-material](https://github.com/vuematerial/vue-material)和以上框架都不同,使用了[编程式方式设置主题](http://vuematerial.io/#/themes/configuration)。 |
| 72 | + |
| 73 | +可以看出在使用变量文件覆盖的方案中,主题变量必须使用框架指定的预处理器定义。 |
| 74 | +例如选择了 vuetify,那开发者自定义的变量文件就必须使用 stylus 来写。这样在实际组件开发中,如果选择 less,就无法使用 stylus 定义的这些变量了。 |
| 75 | +``` vue |
| 76 | +// Component.vue |
| 77 | +
|
| 78 | +<style lang="less"> |
| 79 | + // 引入变量文件 |
| 80 | + @import "@/styles/variables.styl"; |
| 81 | + // 出错了,less 并不认识 stylus 定义的变量 $bg-color |
| 82 | + background: @bg-color; |
| 83 | +</style> |
| 84 | +``` |
| 85 | + |
| 86 | +所以在上述场景中,我们需要将文件中使用 stylus 定义的每一个主题变量都转换成 less 变量,然后注入`.vue`文件的`<style>`块中。 |
| 87 | +如果能实现这一点,其实第一个问题也就顺便解决了。 |
| 88 | + |
| 89 | +## 实现思路 |
| 90 | + |
| 91 | +首先,开发者使用所选 UI 框架的主题解决方案,使用框架指定的预处理器创建一个变量文件,由于该文件只包含变量,对于开发者而言,学习特定预处理器语法的成本并不高。 |
| 92 | + |
| 93 | +然后,使用 loader 处理每一个`.vue`文件。该 loader 接受之前的变量文件作为输入,在每个`.vue`文件的每个`<style>`块中,根据当前`<style>`块指定的预处理器语言,将包含的所有变量进行转换并注入。这样开发者就可以直接使用自己熟悉的预处理器语法开发了。 |
| 94 | + |
| 95 | +并不需要做类似[stylus,less,sass之间全部语法的互相转换](http://csspre.com/convert/)。只需要变量声明这部分。 |
| 96 | + |
| 97 | +*WIP* 但也并不能使用正则简单替换,原因是 stylus 中有 hash 类型的变量声明: |
| 98 | +``` stylus |
| 99 | +$theme := { |
| 100 | + primary: 'white'; |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +所以只能写一个简单的 compiler 做一些词法语法分析的工作。 |
| 105 | + |
| 106 | +## 使用方法 |
| 107 | + |
| 108 | +安装 |
| 109 | +```bash |
| 110 | +npm install theme-variables-loader --save-dev |
| 111 | +``` |
| 112 | + |
| 113 | +在 webpack 中配置规则,处理项目中每一个`.vue`文件,要注意配置`include/exclude`规则,使 loader 只作用于开发者项目内的文件,不包含第三方文件。 |
| 114 | +```javascript |
| 115 | +{ |
| 116 | + test: /\.vue$/, |
| 117 | + use: [ |
| 118 | + { |
| 119 | + loader: 'vue-loader', |
| 120 | + options: vueLoaderConfig |
| 121 | + }, |
| 122 | + { |
| 123 | + loader: 'theme-variables-loader', |
| 124 | + options: { |
| 125 | + injectInVueFile: true |
| 126 | + } |
| 127 | + } |
| 128 | + ], |
| 129 | + include: [resolve('src')] |
| 130 | +}, |
| 131 | +{ |
| 132 | + test: /\.vue$/, |
| 133 | + use: [ |
| 134 | + { |
| 135 | + loader: 'vue-loader', |
| 136 | + options: vueLoaderConfig |
| 137 | + } |
| 138 | + ], |
| 139 | + exclude: [resolve('src')] |
| 140 | +}, |
| 141 | +``` |
| 142 | + |
| 143 | +## 参数说明 |
| 144 | + |
| 145 | +暂定两个参数: |
| 146 | +* `variablesFiles` 变量文件路径,Array 类型 |
| 147 | +* `imports` import 语句,Array 类型 |
| 148 | + |
0 commit comments