webpack入门安装及配置
Javascript模块化
- CommonJS:是
服务器端
模块的规范,Node.js采用了这个规范。根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require
方法,该方法读取一个文件并执行,最后返回文件内部的exports
对象。CommonJS 加载模块是同步
的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。 - AMD规范:是
RequireJS
在推广过程中对模块定义的规范化产出,异步模块定义
,define
定义模板,require引用模板。 - CMD规范:是
SeaJS
在推广过程中对模块定义的规范化产出,module.exports
定义模板,require
引用模板。
备注:CMD推崇依赖就近,AMD推崇依赖前置 - UMD规范:通用模板定义,既兼容AMD也兼容CommonJs,UMD是AMD和CommonJS的糅合。
- AMD模块以
浏览器第一
的原则发展,异步加载模块
。 - CommonJS模块以
服务器第一
原则发展,选择同步加载
,它的模块无需包装(unwrapped modules)。
UMD先判断是否支持Node.js的模块(exports
)是否存在,存在则使用Node.js模块模式。
再判断是否支持AMD(define
是否存在),存在则使用AMD方式加载模块。
webpack介绍
Webpack是一个现代Javascript应用程序
的模块打包器
(module bundler)。当Webpack处理应用程序时,它会递归地构建一个一个依赖关系图,可以把项目的各种js文件
、css文件等
打包合并成一个或多个文件
,其中包含应用程序需要的每个模块,然后将所有这些模块打包成少量的bundle
-通常只有一个,由浏览器加载。
webpack官网:https://www.webpackjs.com/
webpack安装文档参考:https://www.webpackjs.com/guides/installation/
html-webpack-plugin文档参考:https://github.com/jaketrent/html-webpack-template
系统环境安装Node.js最新版本
目的:随同NodeJs集成的包管理工具(npm)
,从NPM服务器
下载别人编写的第三方包
到本地使用。
// 全局安装(一对多)
npm insatll <package name> -g
// 局部安装(一对一)
npm install <package name> --save-dev
安装淘宝镜像:npm install cnpm -g
检查webpack版本:cnpm -v
生成package.json文件
npm init
package.json文件:定义了这个项目所需要的各种模块,以及项目配置信息
(如名称、版本、许可证等元数据)。npm install
命令根据这个配置文件,自动下载所需的模块
。
npm脚本
在package.json
中的script
设置快捷方式,script指定了运行脚本命令的npm命令行缩写:
"script":{
“build”:“webpack”
}
备注:script需要依赖webpack配置文件
先 init 初始化再安装webpack,package.json 就不会产生很多webpack的依赖。
安装webpack和webpack-cli
npm install webpack webpack-cli --save-dev
配置webpack.config.js文件
自己在根目录下新建一个webpack.config.js,全局导出
,所有文件都能访问,以下为该文件的配置信息以及解释:
// path为node自带的路径模板
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 全局导出,让所有的文件都能访问当前的配置信息
module.exports = {
// 开发模式development和生产模式production(一般用开发模式)
mode: "development",
// 入口文件(要打包的文件信息),有三种格式
// String 格式(单个入口文件)
// entry: './src/index.js',
// Array 格式(多个入口文件)
// entry: ['./src/index.js','./src/common.js'],
// Object 格式(多个入口文件)
entry: {
common: './src/common.js',
index: './src/index.js'
},
// 出口文件(要输出打包的文件信息)
output: {
//这里的路径需要绝对路径
path: path.resolve(__dirname,'./dist'),
// filename: 'js/index.js', // 用于单个入口点
// filename: 'js/[name].js', // 用于多个入口点
filename: 'js/[chunkhash].js' // 用于多个入口点,且长效缓存,如71c5d2f31829ccc70a4e.js
},
// 文件监视改动,自动产出build.js,但还是需要手动刷新浏览器
watch: true,
// 插件
plugins: [
new HtmlWebpackPlugin({
title: 'webpack起步', // 改变html中的title文本
template: './index.html', // 打包的html文件,参照物,即模板路径
filename: 'index.html', // 修改输出后的文件名
inject: 'head', // 打包后的html中引入script文件的位置,默认为true,false的话不引入script文件,'head'在头部加载,'body'在头部加载
minify: {collapseWhitespace:true}, // 打包后的html文件去除空格,以及换行符,减小文件大小,节省空间
hash: true, // 做cdn缓存,打包后的js文件后面会加上?标识,做缓存清缓存都通过标识,在本地服务器请求
chunks: ['index'], // 加载哪些入口文件
excludeChunks: ['common'], // 除了哪些文件,其他的都加载
})
]
}
loader加载器
一、样式文件处理加载器
1. css-loader
遇到后缀为.css的文件,webpack先用css-loader加载器解析这个文件,最后计算完的css将会使用style-loader生成一个内容为最终解析完的style标签,放到head标签里面。
// webpack.dev.config.js
module:{
loaders:[
{
test: /\.css$/, // 文件名以.css为后缀的文件
loader: "style-loader!css-loader"
}
]
}
2. less-loader
先安装less才能写.less文件
npm install less-loader -D
-D
安装的开发依赖是辅助工具,版本号会在package.json的devDependencies
显示,-S安装的运行文件在源码之前运行(如vue,vueRouter,babel-polyfill等),版本号会在package.json的dependencies
显示。
// webpack.dev.config.js
// 生命模块,包含多个loader
module:{
loaders:[
{
test: /\.less$/, // "/"是转义字符,“$”是以什么结尾
loader: "style-loader!css-loader!less-loader" //先用less-loader,再用css-loader,最后是style-loader
}
]
}
二、图片文件处理加载器(url-loader和file-loader)
npm install url-loader -D
bse64只会加载一次请求资源。
// webpack.dev.config.js
loaders:[
{
test:/\.(jpg|png|jpeg|gif|svg)$/,
loader: 'url-loader?limit=4000' // limit的值是自己设定的,对于图片大小小于limit的,使用base64编码,可以减少一次图片的网络请求;对于比较大的(超过limit)的图片,就不适合base64,编码和html混在一起,可读性差,html页面大小变大,拖慢了下载页面的速度,所以需要设置合理的limit。
}
]
添加webpack.dev.config.js和webpack.prod.config.js
通常项目会分成三个运行环境:开发人员在本地跑的开发环境(dev)
、测试人员用来做黑盒测试的测试环境(test)
和线上运行的生产环境(production)
。
dev和prod文件的基础代码从webpack.config.js复制过来,然后在package.json修改启动的配置:
"script:"{
// 运行后可以监听文件变化,保持服务器开启状态,不会退出
"dev":"webpack --config ./webpack.dev.config.js",
// 运行后打包退出
"prod":"webpack --config ./webpack.prod.config.js"
}
安装html-webpack-plugin插件
npm install html-webpack-plugin --save-dev
使用目的:之前html中的script标签
所引用的还是写死的index.bundle.js
文件,为了把它们变成动态的
index.html文件,让这个动态生成的index.html文件动态引入
我们打包后生成的js文件。
webpack-dev-server
npm install webpack-dev-server --seve-dev
常见配置参数:
--open
自动打开浏览器
--hot
热更新,不在刷新的情况下替换css
--inline
自动刷新
--port:9999
指定端口
--process
显示编译进度
// package.json
"script":{
"dev":"webpack-dev-server --open --hot --inline --config ./webpack.config.js"
}
ES6的解析
npm install babel-core babel-loader babel-preset-env babel-plugin-transform-runtime -D
babel-loader:转译器
babel-preset-env:预设,env包括es6,7,8等所有版本
loaders:[
{
test:/\.js$/, //处理es6,7,8
loader:'babel-loader',
exclude:/node_modules/, //排除node_modules
options:{
presets:['env'], //处理关键字
plugins:['transform-runtime'] //处理函数
}
}
]
CommonsChunkPlugin的使用
主要用来提取第三方库和公共模块,避免首屏加载的bundle文件或按需加载的bundle文件提及过大,从而导致加载时间过长。
chunk分类:
- entry chunk : 入口文件
- children chunk :入口文件以及它的依赖文件通过代码切割出来的chunk
- common chunk:通过commonsChunkPlugin创建出来的文件
// webpack.dev.config.js
// 场景:有main1和main2两个js入口文件,并且都有导入commonjs和Vue,打包后的main1和main2的js文件,都存在commonjs,vue和webpack的运行文件模块,加大了入口文件的加载速度,所以需要把第三方库vue,公共文件commonjs和webpack运行文件分离出来。
const webpack = require('webpack');
const packagejson = require('./package.json');
module.exports={
entry:{
// 3个入口文件
"main1":"./src/main1.js",
"main2":"./src/main2.js",
"vendor":Object.keys(packagejson.depencies) //获取生产环境依赖的库
},
...
watch: true, // 监听代码改变视图,需要手动刷新
plugins:[
// 第一步为从入口文件中抽离公共的模块,包括第三方库,自定义的公共模块,以及webpack的运行文件,生成vendor.js,"vendor"为自定义的名称
new webpack.optimize.CommonsChunkPlugin({
// 第二步是:“runtime”是指从vendor.js文件中抽离出webpack的运行文件,生成runtime.js
name:['vendor','runtime'],
filename:'[name].js',
// miniChunks属性只有要抽离自定义的公共模块的时候才会用到,不分离则不用写miniChunk属性
miniChunks: Infinity
// 只有当入口文件(entry chunks)>= 3 才生效,用在第三方库中分离自定义的公共模块
});
// 第三步是抽离出common.js
new webpack.optimize.CommonsChunkPlugin({
name:'common', //名称自定义
filename:'[name].js',
// 从main1和main2中抽取出commons chunk
chunks: ['main1','main2']
})
]
}
最终生成的dist文件夹中有:
main1.js
, main2.js
, vendor.js
, runtime.js
,common.js
ensure异步加载(代码切割)
把js模块导出一个.js文件,使用这个模块的时候,webpack会构造script
dom元素,加入到document的head对象中即可,由浏览器发其异步请求这个文件。
// commonJS 规范
//A.js
var A = {"data":"HelloA"}
module.exports = A;
//B.js
var B = {"data":"HelloB"}
module.exports = B;
// 场景:在点击aBtn的时候异步加载A.js,再点击bBtn的时候异步加载B.js
// main.js
import Vue from 'vue';
document.getElementById('aBtn').onclick=function(){
// 异步加载A.js(require是commonJS规范)
require.ensure([],function(){
var A = require('./A.js');
})
}
document.getElementById('bBtn').onClick=function(){
// 异步加载B.js
require.ensure([],function(){
var B = require('./B.js');
})
}
//webpack.dev.config.js
module.exports={
entry:{
// 入口文件
"main":"./src/main.js",
// util是自定义的名称,获取package.json文件中包含的依赖
"util": Object.keys(packagejson.dependencies)
},
plugins:[
// 抽离出公共文件commonjs
new webpack.optimize.CommonsChunkPlugin({
name: "common",
filename:'[name].js'
}),
new HtmlWebpackPlugin({
// 加载哪些入口文件,按顺序加载,公共资源放在前面
chunks:['common','util','main'],
template:"./src/index.html", //参照物,模板路径
inject: true //加载script后,设置放在document的哪个位置(true,head,body,false),其中false为不加载
})
]
}
package.json文件配置以及修改
{
"name": "webpack-test",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"lodash": "^4.17.11",
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3"
}
}
注意:
- 我们还需要调整 package.json 文件,以便确保我们安装包是
私有的(private)
,并且移除 main 入口。这可以防止意外发布你的代码。 - 在此示例中,
<script>
标签之间存在隐式依赖关系
。index.js 文件执行之前,还依赖于页面中引入的lodash
。之所以说是隐式的是因为 index.js 并未显式声明需要引入 lodash,只是假定推测已经存在一个全局变量 _。
npm install lodash --save-dev