返回介绍

三、初探 webpack

发布于 2024-09-07 12:28:43 字数 22419 浏览 0 评论 0 收藏 0

3.1 使用 babel 打包 es6

3.1.1 编译 ES 6/7

Babel

npm install babel-loader@8.0.0-beta.0 @babel/core
npm install –save-dev babel-loader babel-core

Babel Presets

主要有几种类型选择

  • es2015
  • es2016
  • es2017
  • env
  • babel-preset-react
  • babel-preset-stage 0 - 3
npm install @babel/preset-env –save-dev
npm install babel-preset-env –save-dev

Babel Polyfill

针对一些不能处理的函数方法( GeneratorSetMapArray.from... ) 需要用到 babel-Polyfill 处理

  • 全局垫片
  • 为应用准备
npm install babel-polyfill –save
import “babel-polyfill”

Babel Runtime Transform

  • 局部垫片
  • 为开发框架准备
npm install babel-plugin-transform-runtime –save-dev

npm install babel-runtime –save

例子

module.exports = {
entry: {
app: 'app.js'
},
output: {
filename: '[name].[hash:8].js'
},
module: {
rules:[
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',{
//指定 target 为根据哪些语法编译
targets: {
browsers: ['> 1%','last 2 versions']
}
}
]
}
},
exclude: '/node_modules'
]
}
}

对于 webpackbabel 的配置可以单独提取处理 .babelrc 统一管理

{
"presets": [
["@babel/preset-env",{
targets: {
browsers: ['> 1%','last 2 versions']
}
}]
],
"plugins": [
"transform-runtime"
]
]

3.2 打包 Typescript

npm i typescipt ts-loader  --save-dev
npm i typescipt awesome-typescript-loader --save-dev

配置

  • tsconfig.json
  • webpack.config.js

tsconfig

  • 配置选项:官网 /docs/handbook/compiler-options.html
  • 常用选项 compilerOptions include exclude

声明文件

用于编译时检查错误

loadsh 为例,需要安装 @types/lodash 带有声明文件的,而不是安装 lodash

npm install @types/lodash
npm install @types/vue

Typings

也可以这样安装带有 type 的包

npm install typings
typings install lodash

例子

module.exports = {
entry: {
'app': 'app.js'
},
output: {
filename: '[name].bundle.js'
},
module: {
rules: [
test: /\.tsx?$/,
use: {
loader: 'ts-loader'
}
]
}
}

在项目根目录创建 tsconfig.json

{
"compilerOptions": {
"module": "comonjs",
"target": "es5", //编译后的文件在哪个环境运行
"allowJs": true,//允许 js 语法
},
"include": [
//编译路径
"./src/*"
],
"exclude": [
//排除编译文件
"./node_modules"
]
}

3.3 提取 js 的公用代码

  • 减少代码冗余
  • 提高加载速度

主要使用内置插件实现 webpack.optimize.CommonsChunkPlugin

{
plugins: [
new webpack.optimize.CommonsChunkPlugin(option)
]
}

例子

module.exports = {
entry: {
"pageA": "./src/pageA",
"pageB": "./src/pageB",
"vendor": ['loash']//业务代码和第三方代码区分开,给 loash 单独打一个包
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.js'
},
plugins: [
// 提取 common
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
minChunks:2,//出现两次就打包成 common 代码
chunks: ['pageA','pageB']//指定范围提取公共代码
})

// 提取 vendor、取业务代码 manifest
new webpack.optimize.CommonsChunkPlugin({
//把 entry 的 vendor 代码和这里的 common(webpack 打包的)代码合并
names: ['vendor','manifest']
minChunks: Infinity
})
// ==== 提取业务代码 manifest== 合并到上面 names 中
// 如果不想把 webpack 打包的代码和 vendor 代码合并 需要提取到 manifest
//new webpack.optimize.CommonsChunkPlugin({
// webpack 代码和 vendordiam 区分开
// name: 'manifest' //manifest 即生成
// minChunks: Infinity
// })

]
}

3.4 代码分割和懒加载

第一种方式:通过 wepack 内置方法

require.ensure 动态加载一个模块,接收四个参数

  • []:dependencies 初次并不会执行
  • callback 的时候才会执行
  • errorCallback 可省略
  • chunkName

第二种方式:通过 ES2015 Loader Spec

System.import() 后面演变为 import() 来动态加载模块

import() 方式返回一个 promiseimport 中传入需要依赖的明,动态加载模块,就可以像使用 Promise 一样使用 import().then()

代码分割场景

  • 分离业务代码 和 第三方依赖
  • 分离业务代码 和 业务公共代码 和 第三方依赖
  • 分离首次加载 和 访问后加载的代码

例子

module.exports = {
entry: {
"pageA": "./src/pageA"
},
output: {
path: path.resolve(__dirname, './dist'),
publicPath: './dist/',//动态加载路径
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.js'
}
}

目标是提取 pageApageB 中公共的模块 moduleA

//src/pageA.js

import './subPageA'
import './subPageB'

//ensure 的时候代码不会执行 需要在下面加载一次
require.ensure(['lodash'],function(){
var _ = require('lodash')
_.join([1,2,3])
},'vendor')// 指定 chunk 名称


export default 'pageA'

运行打包这时 loadash 提取到 vendor

这时候还不是我们想要的

//src/pageA.js

require.include('./moduleA')

if(page === 'subPageA'){
require.ensure(['./subPageA'],function(){
var subPageA = require('./subPageA')
},'subPageA')// 指定 chunk 名称
}else{
require.ensure(['./subPageB'],function(){
var subPageB = require('./subPageB')
},'subPageB')// 指定 chunk 名称
}


//ensure 的时候代码不会执行 需要在下面加载一次
require.ensure(['lodash'],function(){
var _ = require('lodash')
_.join([1,2,3])
},'vendor')// 指定 chunk 名称


export default 'pageA'

这时候这有 pageA 中才有 moduleA

新建一个 html 验证是否动态加载

<html>
<body>
<script src="./dist/pageA.bundle.js"></script>
</body>
</html>

import() 动态加载的写法

//src/pageA.js

require.include('./moduleA')

var page = 'subPageA'

if(page === 'subPageA'){
// 指定 chunkName /** webpackChunkName: 'subPageA' **/
import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){
console.log(subPageA)
})
}else{
import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){
console.log(subPageB)
})
}

// 异步加载 lodash
//ensure 的时候代码不会执行 需要在下面加载一次
require.ensure(['lodash'],function(){
var _ = require('lodash')
_.join([1,2,3])
},'vendor')// 指定 chunk 名称


export default 'pageA'

如果 /** webpackChunkName: 'subPageA' **/ 相同,则会合并处理

合并了 subPageAsubPageB

来看看打包后的文件,既有 A、B

webpack 代码分割中使用 async 异步加载

module.exports = {
entry: {
"pageA": "./src/pageA",
"pageB": "./src/pageB",
"vendor": ['loash']//业务代码和第三方代码区分开,给 loash 单独打一个包
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.js'
},
plugins: [
// add 异步模块
new webpack.optimize.CommonsChunkPlugin({
aysnc:'async-common',//异步共同的东西
children: true,
names: ['vendor','manifest']
minChunks: Infinity
})


// 提取 vendor、取业务代码 manifest
new webpack.optimize.CommonsChunkPlugin({
//把 entry 的 vendor 代码和这里的 common(webpack 打包的)代码合并
names: ['vendor','manifest']
minChunks: Infinity
})

]
}
//src/subPageA.js


import './moduleA'

console.log('this. is subPageA')

export default 'subPageA'
//src/subPageB.js


import './moduleA'

console.log('this. is subPageB')

export default 'subPageB'
//src/subPageB.js


import './moduleA'

console.log('this. is moduleA')

export default 'moduleA'
//src/pageA.js

// 异步加载不能 include 否则会和 pageA 打包到一起
// require.include('./moduleA')

import _ from 'lodash'

var page = 'subPageA'

if(page === 'subPageA'){
// 指定 chunkName /** webpackChunkName: 'subPageA' **/
import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){
console.log(subPageA)
})
}else{
import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){
console.log(subPageB)
})
}

// === lodash 不在异步加载
//ensure 的时候代码不会执行 需要在下面加载一次
//require.ensure(['lodash'],function(){
// var _ = require('lodash')
// _.join([1,2,3])
//},'vendor')// 指定 chunk 名称


export default 'pageA'
//src/pageB.js

import _ from 'lodash'

var page = 'subPageB'

if(page === 'subPageA'){
// 指定 chunkName /** webpackChunkName: 'subPageA' **/
import(/** webpackChunkName: 'subPageA' **/,'./subPageA').then(function(subPageA){
console.log(subPageA)
})
}else{
import(/** webpackChunkName: 'subPageB' **/'./subPageB').then(function(subPageB){
console.log(subPageB)
})
}

// === lodash 不在异步加载
//ensure 的时候代码不会执行 需要在下面加载一次
//require.ensure(['lodash'],function(){
// var _ = require('lodash')
// _.join([1,2,3])
//},'vendor')// 指定 chunk 名称


export default 'pageB'


这样就把 subpageAsubPageB 共同依赖的 moduleA 异步提取出来

3.5 处理 CSS 和 CSS 模块化

引入 css

需要两个 loaderstyle-loader (创建标签到文档流中)、 css-loader (可以 import 一个样式文件,使得在 js 中可以使用)

Style-Loader

style-loader 除了本身,还有这几个 loader

  • style-loader/url 可以注入 link 标签到页面
  • style-loader/useable 控制样式是否插入到页面中

Style-Loader 的 options

  • insertAt (插入位置)
  • insertInto (插入到 dom
  • singleton (是否只使用一个 style 标签)
  • transform (转化,浏览器环境下,插入页面前)

例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
// loader 解析从后往前处理
rules: [
{
test: /\.css$/,
use: [
{
//loader: 'style-loader',
loader: 'style-loader/url', //使用这个可以往页面注入 link 标签 而不是 style,这个并不常用
},
{
//loader: 'css-loader',
loader: 'file-loader'//使用这个可以往页面注入 link 标签 而不是 style 这个并不常用
}
]
}
]
}
}

CSS-Loader

options

  • alias (解析的别名)
  • importLoader@import
  • Minimize (是否压缩)
  • modules (启用 css-modules

CSS-Modules

localIdentName: '[path][name]__[local]--[hash:base64:5]'

例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
// loader 解析从后往前处理
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
//合并多个 style 为一个
singleton:true
}
},
{
loader: 'css-loader',
options: {
minimize:true,
modules: true,
// css 模块化
localIdentName: '[path][name]_[local]_[hash:base64:5]'
}
}
]
}
]
}
}

配置 Less / Sass

npm install less-loader less  --save-dev
npm install sass-loader node-sass --save-dev

例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
// loader 解析从后往前处理
rules: [
{
test: /\.(css|less)$/,
use: [
{
loader: 'style-loader',
options: {
//合并多个 style 为一个
singleton:true
}
},
{
loader: 'css-loader',
options: {
minimize:true,
modules: true,
// css 模块化
localIdentName: '[path][name]_[local]_[hash:base64:5]'
}
},
{
loader: 'less-loader'
},
{
loader: 'sass-loader'
}
]
}
]
}
}

提取 CSS

  • extract-loader
  • ExtractTextWebpackPlugin

例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
// loader 解析从后往前处理
rules: [
{
test: /\.(css|less)$/,
use:
ExtractTextWebpackPlugin.extra({
// 提取 css 并不会自动加入到文档中,需要在 HTML 手动加入 css 文件
fallback: {
loader: 'style-loader',
options: {
//合并多个 style 为一个
singleton:true
}
},
// 处理 css
use: [
{
loader: 'css-loader',
options: {
minimize:true,
modules: true,
// css 模块化
localIdentName: '[path][name]_[local]_[hash:base64:5]'
}
},
{
loader: 'less-loader'
},
{
loader: 'sass-loader'
}
]
})
}
]
},
plugins: [
new ExtractTextWebpackPlugin({
filename: '[name].min.css',
allChunks: false //指定提取 css 范围,true 所有 import 进来的 css
})
]
}

3.6 PostCSS in Webpack

安装

  • postcss
  • postcss-loader
  • Autoprefixer
  • cssnano
  • postcss-cssnext

例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
// loader 解析从后往前处理
rules: [
{
test: /\.(css|less)$/,
use:
ExtractTextWebpackPlugin.extra({
// 提取 css 并不会自动加入到文档中,需要在 HTML 手动加入 css 文件
fallback: {
loader: 'style-loader',
options: {
//合并多个 style 为一个
singleton:true
}
},
// 处理 css
use: [
{
loader: 'css-loader',
options: {
minimize:true,
modules: true,
// css 模块化
localIdentName: '[path][name]_[local]_[hash:base64:5]'
}
},
{
loader: 'less-loader'
},
{
loader: 'sass-loader'
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('antoprefixer')()
]
}
}
]
})
}
]
},
plugins: [
new ExtractTextWebpackPlugin({
filename: '[name].min.css',
allChunks: false //指定提取 css 范围,true 所有 import 进来的 css
})
]
}

3.7 Tree shaking

3.7.1 JS Tree shaking

使用场景

  • 常规优化
  • 引入第三方库的某一个功能

    例子

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['env'],
plugins: [
// lodash Tree shaking
'lodash'
]
}
}
]
}
]
},
plugins: [
new ExtractTextWebpackPlugin({
filename: '[name].min.css',
allChunks: false //指定提取 css 范围,true 所有 import 进来的 css
})
// Tree shaking
new webpack.optimize.UglifyJsPlugin({

})
]
}

有些库不是 es 模块写的,并不能 tree shaking 。需要借助其他工具

npm i babel-loader babel-core babel-preset-env babel-plugin-lodash --save

lodash Tree 不生效

  • lodash-es –> no
  • babel-lugin-lodash —>working

查看模块是否 Tree Shaking 方式:去第三方库 index.js 中看模块书写方式是否是 es

3.7.2 CSS Tree shaking

主要使用 purifycss-webpack

例子

const glob = require('glob-all')

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/', //指定从项目中哪里引入资源
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['env'],
plugins: [
// lodash Tree shaking
'lodash'
]
}
}
]
}
]
},
plugins: [
new ExtractTextWebpackPlugin({
filename: '[name].min.css',
allChunks: false //指定提取 css 范围,true 所有 import 进来的 css
})

// 放到 ExtractTextWebpackPlugin 后面
new PurifyCss({
paths: glob.sync([
path.join(__dirname,'./*.html'),
path.join(__dirnname,'./src/*.js')
])
})

// Tree shaking
new webpack.optimize.UglifyJsPlugin({

})
]
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文