模块化&webpack
大纲
- 模块化
- webpack
模块化
- 全局 function模式
js
function m1(){}
function m2(){}
- namespace 模式
js
let myModule = {
data:"test",
foo(){
}
}
- IIFE 模式 匿名函数自调用
js
(function(window){
let data = "test";
function test(){
}
windos.module = {
data,test
}
// TODO jQuery
})(window)
- IIFE 模式增强 引入依赖
js
(function(window,$){
let data = "test";
function test(){
}
windos.module = {
data,test
}
// TODO jQuery
})(window,jQuery)
- 模块化规范 commonjs node - 运行时 暴露模块 module.exports = {} 引入模块 require('')
- AMD CMD ES6 UMD ES6 - 编译时 基本使用 export import xx from xx
webpack
webpack.config.js
js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 从css文件中提取css代码到单独的文件中,对css代码进行代码压缩等
const MiniCssExtraPlugin = require('mini-css-extra-plugin');
module.exports = {
mode:'development',
// 单入口
// entry: path.resolve(__dirname,'../../main.js'),
// 如果多入口(生成多个html),要配合下面的plugins
entry: {
main:path.resolve(__dirname,'../../main.js'),
header:path.resolve(__dirname,'../../header.js'),
},
output:{
filename: '[name].[hash:8].js',
path: path.join(__dirname,'../dist'),
clean:true
},
module:{
rules:[
{
test:/\.css$/,
// use:['style-loader','css-loader'], //这里如果使用了MiniCssExtraPlugin就不能使用'style-loader'
use:[MiniCssExtraPlugin.loader,'css-loader'],
},
{
test:/\.less$/,
// use:['style-loader','css-loader','less-loader'], //顺序不能错,从后往前
use:[MiniCssExtraPlugin.loader,'css-loader','less-loader'],
},
{
test:/(\.jsx|.js)$/,
use:['babel-loader',path.resolve(__dirname,'../loader/drop-console.js')], //此处引用的下文的drop-console.js
exlude:/node_modules/,
},
]
},
plugins:[
new HtmlWebpackPlugin({
template: path.relative(__dirname,'../publice/index,html'),
title:'webpack0507',
chunks:['main'],
filename:'index.html'
}),
new HtmlWebpackPlugin({
filename:'header.html',
chunks:['header'],
}),
new MiniCssExtraPlugin({
filename: '[name].[hash:8].css',
chunkFilename:'[id].css'
}),
],
}
babel
es6语言降级
@babel/core babel 核心库 babel-loader @babel/preset-env 一组预先设定的插件 默认支持所有的最新js(包括ES6 ES6+) @babel/plugin-transfom-runtime async await import 提前把垫片加载到环境中
loader
编写原则
- 单一原则-每个loader只做一件事情
- 链式调用 webpack会按照顺序链调用每个loader
- 统一原则 输入或者输出都是字符串,即插即用
- input -> @babel/parser -> ast
- ast -> @babel/traverse -> new ast
- ast -> @babel/generate -> output
@babel/types 对节点进行分析
举例:
手写drop-console.js
js
// 去除代码中的console.log
// ast
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types')
module.exports = function (source) {
const ast = parser.parse(source, { sourceType: 'module' });
traverse(ast, {
CallExpression(path) {
if (
t.isMemberExpression(path.node.callee) &&
t.isIdentifier(path.node.callee.object, { name: 'console' })) {
path.remove();
}
}
})
const output = generator(ast, {}, source);
return output.code;
}
plugin
webpack继承tapable类:
- tap 以同步方式触发钩子
- tapAsync 以异步方式触发钩子
- tabPromise 以异步方式触发钩子,返回promise
整个webpack会使用所有配置信息生成一个compiler
然后执行run
compilation 对象
实现一个MyPlugin
js
class MyPlugin(){
constructor(){
}
apply(compiler){
compiler.hook.done.tap('MyPlugin',()=>{
console.log('编译完成')
})
compiler.hook.emit.top('MyPlugin',(complilation)=>{
let assets = complilation.assets
let content = '';
Object.entries(assets).forEach((filename,statObj)=>{
content += `文件名: ${filename} 文件大小: ${statObj.size()} \n`
})
console.log(content)
})
}
}
module.exports = MyPlugin;
use
js
plugins:{
new MyPlugin(),
}