[TOC]
本质上webpack是一个现代javascript应用程序的静态打包工具。 其它基础概念,查看官方文档
webpack 只能理解javascript和json文件。loader可以让它能够处理其他类型的文件,并将其转换有效模块。
两者主要功能都是对加载资源进行打包,但是 url-loader设置资源大小来对小资源进行打包js内,来减少网络带宽
module: {// rules: [// {// test: /\.(png|jpg|gif)$/,// use: {// loader:'file-loader',// options:{// name:'[name].[ext]'// }// }// },{test: /\.(png|jpg|gif)$/,use: [{loader: 'url-loader',options:{limit:138228 //资源大小}}]},]},
***:**最终在浏览器端可以看到打包后得资源路径变为 'data:image/png;base64...'
安装
npm i style-loader css-loader -D
使用
{test:/\.css$/,use:["style-loader",{loader:"css-loader",options:{importLoaders:2 //保证import 引入得样式会执行所有loader,modules:true //样式模块化}}] //注意loader 顺序}
css-loader 用户处理.css 文件,style-loader用于对样挂在标签上。 注意loader顺序, loader是从下到上 从右到左开始加载。
file-loader 和 url-loader 可以接收并加载任何文件也包括字体.
{test:/\.(eot|svg|ttf|woff)$/, //打包字体use:["file-loader"]},
可以在webpack 运行到某个时刻帮我们做一些事情比如:打包优化、资源管理、注入环境变量等。
plugins:[new cleanWebpackPlugin(), //预先清理new htmlWebpackPlugin({title:'html plugin demo',template:'index.html'})],
它可以识别某些类别的webpack错误,并清理,聚合和优先级,以提供更好的开发人员体验。
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');plugins:[new FriendlyErrorsPlugin({compilationSuccessInfo: {messages: ['You application is running here http://localhost:3000'],notes: ['Some additionnal notes to be displayed unpon successful compilation']},onErrors: function (severity, errors) {// You can listen to errors transformed and prioritized by the plugin// severity can be 'error' or 'warning'},// should the console be cleared between each compilation?// default is trueclearConsole: true,// add formatters and transformers (see below)additionalFormatters: [],additionalTransformers: []})]
const WebpackBuildNotifierPlugin=require('webpack-build-notifier');{devServer:{//...quiet:true},plugins:[new WebpackBuildNotifierPlugin({title: "My Project Webpack Build",// logo: path.resolve("./img/favicon.png"),suppressSuccess: true}),]}
sourceMap:它是一个映射关系,可以找到打包后代码位置在打包前哪个位置.配置
devtool:'source-map' //production 环境默认配置
记忆技巧
它帮我们启动一个简单web server,并有实时重新加载功能.
npm i webpack-dev-server -D
devServer:{port:8080, //启动端口号contentBase:'./dist', //从什么位置查找文件open:true, //自动打开默认浏览器,并访问服务proxy:{ //跨域代理'/api':'http://localhost:3000'},historyApiFallback:true //开启浏览器路由}
webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。
npm install wepack-dev-middleware --save-dev
根目录新建server.js
/*** 实现类似 devServer服务,作用:1.监听文件改变* 1. 检测文件改变'* 2. 读取webpack conifg 文件* 3. 启动一个服务* */const express=require('express');const webpackDevMiddleware=require('webpack-dev-middleware');const webpack=require('webpack');const config=require('./webpack.config.js');const complier=webpack(config);const app=express();// 告诉 express 使用 webpack-dev-middleware,// 以及将 webpack.config.js 配置文件作为基础配置app.use(webpackDevMiddleware(complier,{publicPath:config.output.publicPath}));app.listen(3000,()=>{console.log('server at port 3000');})
package.json
script:{"server":"node server.js"}
启动server
npm run server
它会在应用程序运行过程中,替换、添加或删除模块,而不需要重新加载整个页面。
const webpack=require('webpack');devServer"{hot:true, //启动热替换hotOnly:true //当 hot不起作用时,浏览器也不会重新}plugins:[new webpack.hotModlueReplacementPlugin()]
分别创建webpack.dev.js 和webpack.prod.js用于表示开发环境和生产环境配置。
"script":{"dev":"webpack-dev-server --config webpack.dev.js","build":"webpack --config webpack.prod.js"}
我们还可以通过 webpack-merge 来将 dev 和pro 文件公共部分提取出来。
//webpack.dev.jsconst merge=require('webpack-merge');const commConfig=require('./webpack.comm.js');const devConfig={...}module.exports =merge(commConig,devConfig);
此特性可以把代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。 常用的代码分离方法有三种:
入口配置:使用 entry 配置手动地分离代码。 防止重复:使用 SplitChunksPlugin 去重和分离 chunk。 动态导入:通过模块中的内联函数调用来分离代码。
entry:{ //配置多个入口文件index:'./src/index.js',another:'./src/modleA.js'},
通过配置多个入口文件,则可以生成多个chunk,但是如果每个入口文件(chunk)之间存在重复的模块,则会导致被重复引用进各个模块中。我们可以通过使用 SplitChunksPlugin 插件来移除重复模块。
该插件已经集成在webpack中,不需要再次安装,现在我们修改上面配置
module.exports = {mode:'development',entry:{index:'./src/index.js',// another:'./src/modleA.js' //去掉手动引入loadsh 的入口文件},optimization:{ //添加如下配置splitChunks:{chunks:'all'}},}
function getComponent() {return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {var element = document.createElement('div');element.innerHTML = _.join(['Hello', 'webpack'], ' ');return element;}).catch(err => 'An error occured while loading the component');}getComponent().then(component => {document.body.appendChild(component);})
//webpack.config.jsmodule.exports = {mode: 'development',entry: {index: './src/index.js',},plugins: [new cleanWebpackPlugin(),new htmlWebpackPlugin({template: 'index.html'}),],
懒加载可以帮助我们优化网页或应用程序提升加载速度。 modleA.js
console.log('this modlle A ');export default ()=>{console.log('button click hered');}
index.js
function initComponent() {var button = document.createElement('button');var br = document.createElement('br');button.innerHTML = 'click me!';document.body.appendChild(br);document.body.appendChild(button);button.addEventListener('click', () => {import(/* webpackChunkName: "modleA" */ './modleA').then(module => {var print = module.default;print();});})}initComponent();
在webpack中,所有都是模块化,因此模块与模块之间是独立的,如果我们想运行下面这样代码,此时就需要shim 配置全局变量
//juery.test.jsexport defualt ()=>{$('body').css('background':'green');}
import $ from 'jquery'import test from './juqery.test.js'test();
配置webpack
const webpack=require('webpack');plugins:[new webpack.providePlugin({$:'juqery'})]
//安装 webpack依赖
npm install --save-dev webpack webpack-cli npm install --save-dev typescript ts-loader
项目根目录新建tsconfig.json文件
{"compilerOptions": {"outDir": "./dist/","noImplicitAny": true,"module": "es6","target": "es5","jsx": "react","allowJs": true}}
const path = require('path');module.exports = {entry: './src/index.ts',module: {rules: [{test: /\.tsx?$/,use: 'ts-loader',exclude: /node_modules/}]},resolve: {extensions: [ '.tsx', '.ts', '.js' ]},output: {filename: 'bundle.js',path: path.resolve(__dirname, 'dist')}};
entry:{index:'./src/index.ts',list:'./src/list.ts'},plugins:[new HtmlWebapckPlugin({fileName:'index.html',chunks:['runtime','index'],}),new HtmlWebapckPlugin({filename:'list.html',chunks:['runtime','list'],}),]
关于配置函数化提取,和配置。
const makePlugins=(configs)=>{const plugins=[new CleanWebpackPlugin(['dist'],{root:path.resolve(__dirname,'dist');})];Object.keys(configs.entry).forEarch(item=>{plugins.push(new HtmlWebapckPlugin({fileName:`${item}.html`,chunks:['runtime',item],}))});return plugins;}const config={entry:{index:'./src/index.ts',list:'./src/list.ts'},};config.configs=makePlugins(config);module.exports= config;
//安装loader 和babel 核心库npm install --save-dev babel-loader @babel/core//语法转换库npm install --save-dev @babel/preset-env//添加webpack 配置module:{rules:[{test: /\.js$/,exclude: /node_modules/,use:{loader: 'babel-loader',options:{presets:['@babel/preset-env']}}}]}
到这里,es6语法基本可以转换,还是对于map、includes 等方法函数,没有转换, 如果要进行转换则需要babel-profill 使用流程如下:
npm install --save @babel/polyfill
import "@babel/polyfill";
到这里,打包后的代码会被转化,但是babel-polyfill 里有所以规则导致打包后体积变很大, 如果我们只是想转换我们写的代码,则需要修改如下配置
options:{presets:[['@babel/preset-env',{useBuiltIns:'usage'}]]}
使用 @babel/plugin-transform-runtime来进行转换
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime-corejs2 2. .baberc
{"plugins": [["@babel/plugin-transform-runtime",{"absoluteRuntime": false,"corejs": 2, //默认false"helpers": true,"regenerator": true,"useESModules": false}]]}
两者都能提升code coverage,prefetch 是指将来某个节点需要加载的资源;preload 是指当前可能需要加载的资源
import(/* webpackPrefetch: true */ 'LoginModal');import(/* webpackPreload: true */ 'ChartingLibrary');