以前的我做事很看重目的, 后来感觉这样比较累, 其实很多事不需要目的. 你做一件事, 可以就是玩, 跟着感觉走是最好的选择.
其实接触项目工程化已经有两年了, 然而由于工作的职责大多是写组件, 或者研究各种库的应用, 实现页面效果, 其实对于 webpack 配置一直没怎么研究. 最近有时间研究一下, 看了自定义 loader 的实现, 但是不知道用来干吗, 那就不妨就随便写点吧.
新建 demo01
loader.JS
- let utils = require('loader-utils')
- module.exports = function loader(source) {
- const options = utils.getOptions(this)
- return `export const data = {title: '${options.title}', list: '${JSON.stringify(source)}'}`
- }
这是 loader 最基本的用法, source 参数是文件的字符串. 拿到之后你要做什么, 就看你的了.
引入 loader-utils 模块是为了获取 webpack 配置中的 options 选项
webpack.config.JS
- let path = require('path')
- module.exports = {
- entry: './main.js',
- output: {
- filename: 'bundle.js'
- },
- module: {
- rules:[
- {
- test: /\.(txt)$/,
- use: [{
- loader: path.resolve('./loader'),
- options: {
- title: '花名册'
- }
- }]
- }
- ]
- }
- };
我准备了一个 txt 文件, 内容很简单
张三, 20, 男 | 李四, 23, 女
在 main.JS 中引入
- import {
- data
- } from './test.txt'
- document.write('<h1>' + data.title + '</h1><div>' + data.list + '</div>');
让我们再来明确一下 loader 到底做了什么事情.
运行
NPM run build
这时会生成一个 bundle.JS 文件, 看看里面, 其实模块会被转换成这样的 JS
- (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- const data = {title: '花名册', list: '"张三, 20, 男 | 李四, 23, 女"'}
- __webpack_exports__["a"] = data;
- })
猜测一下 loader 的转换过程, loader 的参数是一个字符串, 而它返回的也是一个字符串, 因为它可能需要提供给下一个 loader 去处理.(例如 CSS-loader 和 style-loader) 最后的 loader 输出的应该是 JS 模块的文本形式, 被 webpack 转换成 JS 文件.
优化
上面的例子似乎没什么大的作用, 我来改造一下.
- let utils = require('loader-utils')
- module.exports = function loader(source) {
- const options = utils.getOptions(this)
- const strList = source.split('|')
- const list = strList.map(r => {
- const data = r.split(',')
- return {
- name: data[0],
- age: data[1],
- sex: data[2]
- }
- })
- return `export const data = {title: '${options.title}', list: ${JSON.stringify(list)}}`
- }
loader 对字符串进行解析, 转化成数组形式
main.JS
- import {data} from './test.txt'
- let list = data.list.map(r => {
- return '<li>' + r.name + '/' + r.age + '/' + r.sex + '</li>'
- })
- document.write('<h1>' + data.title + '</h1><ul>' + list.join('') +'</ul>');
在入口文件引入组装成 dom.
运行
NPM run dev
效果如下:
效果图. PNG
这样就实现了将 txt 文件的内容转换成数组引入到组件中使用.
再运行
NPM run build
发现命令行报错
- ERROR in bundle.JS from UglifyJs
- Unexpected token: name (list) [bundle.JS:74,4]
那是因为 main.JS 中包含了 es6 语法, 需要在 webpack 中配置相关 loader. 虽然如此, 在高版本 Chrome 浏览器运行不会出现错误, 因为 Chrome 本身支持 es6 部分语法. 我们还是修改一下 webpack 配置吧
- let path = require('path')
- module.exports = {
- entry: './main.js',
- output: {
- filename: 'bundle.js'
- },
- module: {
- rules:[
- {
- test: /\.JS[x]?$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['es2015', 'react']
- }
- }
- },
- {
- test: /\.(txt)$/,
- use: [{
- loader: path.resolve('./loader'),
- options: {
- title: '花名册'
- }
- }]
- }
- ]
- }
- };
demo02-Loader 的内联方式
工具的不断完善过程就是提出问题和解决问题的过程. 我给自己提出一个问题. 现在我用 | 和, 作为分隔符, 如果我有另一个 test2.txt 文件, 是这样的
王五 * 21 * 男, 赵六 * 28 * 女
它的分隔符变成了, 和 *, 但我需要在同一个 Loader 实现, 应该怎么做呢? 很容易想到, 应该要在 loader 使用时传参.
我们先试试原先的 title 传参行不行.
例如:
import {data} from './loader.js?title = 测试标题!./test.txt'
发现结果很怪异. 如果 loader 里面有打印信息, 你会发现 loader 调用了两次.
- {
- title: '花名册'
- }
- {
- title: '标题'
- }
- Hash: 6747a1f812418d4f8c20
- Version: webpack 3.10.0
原因是在 webpack.config.JS 中使用了 loader.import 引入时, 是从右往左处理的, 引入 text.txt 文件时, webpack 会自动调用在 webpack.config.JS 中的 loader, 然后处理到 ./loader.JS 文件时, 又调用了一次. 因此 webpack.config.JS 里面应该删掉自定义 loader 的使用.
- let path = require('path')
- module.exports = {
- entry: './main.js',
- output: {
- filename: 'bundle.js'
- },
- module: {
- rules:[
- {
- test: /\.JS[x]?$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['es2015', 'react']
- }
- }
- }
- // ,
- // {
- // test: /\.(txt)$/,
- // use: [{
- // loader: path.resolve('./loader'),
- // options: {
- // title: '花名册'
- // }
- // }]
- // }
- ]
- }
- };
既然成功了. 我们可以将 loader 改造一下:
- let utils = require('loader-utils')
- module.exports = function loader(source, map) {
- const options = utils.getOptions(this)
- let split1 = '|', split2 = ',';
- if(options.type === '2') {
- split1 = ','
- split2 = '*'
- }
- const strList = source.split(split1)
- const list = strList.map(r => {
- const data = r.split(split2)
- return {
- name: data[0],
- age: data[1],
- sex: data[2]
- }
- })
- return `export const data = {title: '${options.title}', list: ${JSON.stringify(list)}}`
- }
代码很简单, 如果传入的 type 是 2 时, 就使用另一套分隔符.
main.JS 中则这样写:
- import {data} from './loader.js?title = 合并列表 & type=1!./test.txt'
- import {data as data2} from './loader.js?type=2!./test2.txt'
- let list = data.list.map(r => {
- return '<li>' + r.name + '/' + r.age + '/' + r.sex + '</li>'
- })
- let list2 = data2.list.map(r => {
- return '<li>' + r.name + '/' + r.age + '/' + r.sex + '</li>'
- })
- document.write('<h1>' + data.title + '</h1><ul>' + list.join('') + list2.join('') + '</ul>');
效果 OK
合并列表. PNG
项目相关地址: Git 地址
如果后面有新的想法或许会在这个项目更新
来源: http://www.jianshu.com/p/1fd62b78baff