首先我们先回顾一下webpack常见配置,因为后面会用到,所以简单介绍一下。
1.1 webpack常见配置
// 入口文件 entry: { app: './src/js/index.js', }, // 输出文件 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: 'https://www.jb51.net/' //确保文件资源能够在 :3000 下正确访问 }, // 开发者工具 source-map devtool: 'inline-source-map', // 创建开发者服务器 devServer: { contentBase: './dist', hot: true // 热更新 }, plugins: [ // 删除dist目录 new CleanWebpackPlugin(['dist']), // 重新穿件html文件 new HtmlWebpackPlugin({ title: 'Output Management' }), // 以便更容易查看要修补(patch)的依赖 new webpack.NamedModulesPlugin(), // 热更新模块 new webpack.HotModuleReplacementPlugin() ], // 环境 mode: "development", // loader配置 module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] }
这里面我们重点关注 module和plugins属性,因为今天的重点是编写loader和plugin,需要配置这两个属性。
1.2 打包原理
识别入口文件
通过逐层识别模块依赖。(Commonjs、amd或者es6的import,webpack都会对其进行分析。来获取代码的依赖)
webpack做的就是分析代码。转换代码,编译代码,输出代码
最终形成打包后的代码
这些都是webpack的一些基础知识,对于理解webpack的工作机制很有帮助。
2 loader
OK今天第一个主角登场
2.1 什么是loader?
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
处理一个文件可以使用多个loader,loader的执行顺序是和本身的顺序是相反的,即最后一个loader最先执行,第一个loader最后执行。
第一个执行的loader接收源文件内容作为参数,其他loader接收前一个执行的loader的返回值作为参数。最后执行的loader会返回此模块的JavaScript源码
2.2 手写一个loader
需求:
处理.txt文件
对字符串做反转操作
首字母大写
例如:abcdefg转换后为Gfedcba
OK,我们开始
1)首先创建两个loader(这里以本地loader为例)
为什么要创建两个laoder?理由后面会介绍
reverse-loader.js
module.exports = function (src) { if (src) { console.log('--- reverse-loader input:', src) src = src.split('').reverse().join('') console.log('--- reverse-loader output:', src) } return src; }
uppercase-loader.js
module.exports = function (src) { if (src) { console.log('--- uppercase-loader input:', src) src = src.charAt(0).toUpperCase() + src.slice(1) console.log('--- uppercase-loader output:', src) } // 这里为什么要这么写?因为直接返回转换后的字符串会报语法错误, // 这么写import后转换成可以使用的字符串 return `module.exports = '${src}'` }
看,loader结构是不是很简单,接收一个参数,并且return一个内容就ok了。
然后创建一个txt文件
2)mytest.txt
abcdefg
3)现在开始配置webpack
module.exports = { entry: { index: './src/js/index.js' }, plugins: [...], optimization: {...}, output: {...}, module: { rules: [ ..., { test: /\.txt$/, use: [ './loader/uppercase-loader.js', './loader/reverse-loader.js' ] } ] } }
这样就配置完成了
4)我们在入口文件中导入这个脚本
为什么这里需要导入呢,我们不是配置了webapck处理所有的.txt文件么?
因为webpack会做过滤,如果不引用该文件的话,webpack是不会对该文件进行打包处理的,那么你的loader也不会执行
import _ from 'lodash'; import txt from '../txt/mytest.txt' import '../css/style.css' function component() { var element = document.createElement('div'); var button = document.createElement('button'); var br = document.createElement('br'); button.innerHTML = 'Click me and look at the console!'; element.innerHTML = _.join('【' + txt + '】'); element.className = 'hello' element.appendChild(br); element.appendChild(button); // Note that because a network request is involved, some indication // of loading would need to be shown in a production-level site/app. button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => { var print = module.default; print(); }); return element; } document.body.appendChild(component());
package.json配置