Webpack基本使用(详解)
2021-05-06 11:27
在开始之前,我们先来看以下Webpack官网首页的图片,并思考一下官网图片传达给我们的含义?
一.Webpack基本介绍
1.1 概念的引入
思考:在网页中,我们经常会引入哪些常见的静态资源
- JS
- .js .jsx .coffee .ts(TypeScript 类 C# 语言)
- CSS
- .css .less .sass .scss
- Images
- .jpg .png .gif .bmp .svg
- 字体文件(Fonts)
- .svg .ttf .eot .woff .woff2
- 模板文件
- .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】
问题:网页中静态资源多了以后存在的问题
- 网页加载速度变慢,因为要多次重复的发送资源请求
- 要处理错综复杂的依赖关系
如何解决上述两个问题?
- 合并、压缩、精灵图、图片的Base64编码
- 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系;
1.2 什么是Webpack
Webpack是一个前端的项目构建工具,它是基于node.js开发出来的一个前端工具
如何实现上述的2种解决方案?
- 使用Gulp, 是基于 task 任务的;
- 使用Webpack, 是基于整个项目进行构建的;
- 借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。
- 根据官网的图片介绍webpack打包的过程
- webpack官网
1.3 Webpack支持的规范
Webpack支持以下规范
- CommonJS规范
//moduleA.js 导出 module.exports = function(){ //... } //moduleB.js 导入 var moduleA = require(‘./moduleA‘)
- AMD规范(推崇依赖前置)
//moduleA.js 导入和导出 define([‘jquery‘,‘./math.js‘],function($,math){ //AMD是依赖前置,将文件的依赖通过数组的形式导入,然后当作函数的参数传递进函数使用 //通过return来实现对外接口 return helloWorld })
-
CMD规范(推崇就近依赖,需要用到的时候再去加载模块)
标准语法:define(id?,deps?,factory)
- 一个文件一个模块,所以经常用文件名作为模块id
- CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写
- factory是一个函数,该函数拥有三个参数 function(require,exports,module)
- require:一个方法,接收模块标识,用来获取其它模块提供的接口
- exports:一个对象,用来向外提供模块接口
- module:一个对象,存储了与当前模块相关联的一些属性和方法
define(fcuntion(require,exports,module){
var $ = require(‘jquery.js‘)
})
复制代码
- ES6规范
- 在ES6规范中,使用import和exports命令来导入和导出文件
//moduleA.js 导出 //...内容区 //导出函数(还可以导出对象以及任何你想导出的数据类型) exports.func = someFunc //moduleB.js 导入 import func from ‘./moduleA‘
掘金:AMD和CMD的区别
1.4 安装Webpack
- 运行
npm i webpack -g
全局安装Webpack,这样就能在全局使用Webpack命令- 注意,如果是
webpack4.0
以上的版本,需要全局安装Webpack-cli
- 注意,如果是
- 在项目根目录运行
npm i webpack --save-dev
安装到项目依赖中
1.5 命令行的使用
- 查看Webpack版本信息
npm info webpack
- 安装指定版本的Webpack
npm install webpack@版本号
- 卸载webpack
npm uninstall webpack webpack-cli -g
二.Webpack基本使用
2.1 Webpack基本的使用方式-实例
目标:使用Webpack打包构建列表隔行变色案例
-
创建基本的目录结构
webpack-study
- dist
- src
- js
- css
- images
- index.html
- main.js
-
在项目目录下面运行
npm init
初始化项目 -
使用
npm i jquery --save
安装jquery类库 -
创建main.js并书写各行变色的代码逻辑
main.js //导入jquery内库 import $ from ‘jquery‘ $(function(){ $("li:odd").css(‘backgroundColor‘,‘red‘) $("li:even").css(‘backgroundColor‘,‘tomato‘) })
-
在页面直接引用main.js会报错,因为浏览器不认识ES6的新语法import,需要使用Webpack进行处理,Webpack默认会把这种高级语法转换为低级浏览器能够识别的语法
-
运行
webpack 入口文件路径 输出文件路径
对main.js进行处理webpack ./src/main.js ./dist/bundle.js
-
在index.html中引入bundle.js代替main.js
2.2 Webpack基本配置
问题描述:
-
每次修改文件之后,都要使用
webpack 入口文件路径 输出文件路径
来对文件进行处理,使用起来比较繁琐
期待实现:
- 直接在控制台输入 webpack 命令,即可自动完成文件的处理
实现方法
- 在项目根目录添加Webpack的配置文件 webpack.config.js
- 在Webpack配置文件里面配置处理的入口文件和输出文件
- 配置完成之后,即可在调试台通过 webpack命令来对文件进行处理
const path = require(‘path‘) module.exports = { entry:path.join(__dirname,‘./src/main.js‘), output:{ path:path.join(__dirname,‘./dist‘), filename:‘bundle.js‘ } }
三.webpack-dev-server的使用
3.1 实现自动打包编译
问题描述:每次改完代码,都需要手动执行webpack
命令打包编译文件,比较繁琐
目标:每次改完代码,我们点击保存之后就可以帮我们自动打包编译
-
安装
webpack-dev-server
- -D 将该插件的依赖写入开发依赖中
npm install webpack-dev-server -D
-
在
package.json
里面的scripts
属性里面添加webpack-dev-server
命令到开发环境"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server" },
-
在本地安装webpack,webpack-dev-server想要在本地项目中运行,必须在项目中也安装 webpack
npm install webpack -D
-
执行
npm run dev
命令即可,会有如下返回
-
由第四步可知,webpack-dev-server帮我们生成的bundle.js运行于项目根目录,这个文件并没有存到物理磁盘上,而是托管到了电脑内存中,所以我们在项目中根本看不到这个bundle.js文件,但是我们可以通过将index.html中的bundle.js引用路径修改为项目根路径,即可引用到该文件
-
可以认为
webpack-dev-server
把打包好的文件,以一种虚拟的形式,托管到了项目的根目录中,虽然我们看不到它,但是可以认为和dist,src,nodemodule
,平级,有一个看不见的文件,叫做bundle.js
注意:
- 若安装过程中有中断,需要把 node-modules文件夹删掉,重新执行 npm install下载依赖,否则会报错
- 最后执行npm run dev 之后,就会开始自动监听我们的修改,每次修改保存都会触发自动打包编译
3.2 额外的参数
webpack-dev-server
除了帮我们实现自动编译打包的功能之外,还可以添加 额外的参数帮我们实现更强大的功能
方式1(推荐):
- 项目启动后自动打开浏览器:
--open
- 指定项目端口号:
--port 3000
- 指定项目启动后的主页面:
--contentBase src
- 指定热加载:
--hot
- 不加热加载之前,每次修改都会生成一个新的bundle.js
- 加了热加载之后,每次都会在原基础上更新bundle.js,提升 效率
//package.json "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server --open --port 3000 --contentBase src --hot" },
方式2(了解即可):
//webpack.config.js const webpack = require(‘webpack‘) //引入Webpack,启用热更新的第2步 devServer:{ //设置dev-server命令参数的第二种形式,相对麻烦一些 open:true, port:3000, contentBase:‘src‘, hot:true // 启用热更新的第一步 }, plugins:[ //配置插件的节点,热更新的第2步 new webpack.HotModuleReplacementPlugin() //new 一个热更新的模块对象,这是启用热更新的第3步 ]
3.3 html-webpack-plugin插件
插件作用:
- 在内存中,生成HTML页面的插件
- 将打包好的bundle.js插入页面中去,无需我们手动引入bundle.js
插件用法:
-
安装 html-webpack-plugin插件
npm i html-webpack-plugin -D
-
在webpack.config.js文件中添加插件
- 配置模版页面,即根据该模版页面生成内存中的页面
- 指定生成页面的名称
//webpack.config.js const htmlWebpackPlugin = require(‘html-webpack-plugin‘) plugins:[ new htmlWebpackPlugin({ //创建一个在内存中生成html页面的插件 template:path.join(__dirname,‘./src/index.html‘),//指定模版页面,将来会根据指定的页面路径,去生成内存中的页面 filename:‘index.html‘ }) ]
实际展示:
拓展:-S,-D,-g说明
npm install module_name -S 即 npm install module_name –save 写入dependencies
npm install module_name -D 即 npm install module_name –save-dev 写入devDependencies
npm install module_name -g 全局安装(命令行使用)
npm install module_name 本地安装(将安装包放在 ./node_modules 下)
dependencies与devDependencies有什么区别呢?
- devDependencies 里面的插件只用于开发环境,不用于生产环境
- dependencies 是需要发布到生产环境的
四.loader的使用
注意:Webpack默认只能打包处理Js类型的文件,无法处理其它的非Js类型的文件
如果要处理非Js类型的文件,我们需要手动安装一些合适的第三方loader加载器
4.1 loader处理样式表
-
打包处理CSS文件:安装 style-loader css-loader
npm i style-loader css-loader -D
-
在
webpack.config.js
配置文件里面新增module
节点对象,在这个module
对象身上,有个rules
属性数组,这个数组中,存放了所有的第三方文件爱你的匹配和处理规则module:{ //这个节点,用来配置所有第三方模块加载器 rules:[ //配置第三方模块的匹配规则 {test:/\.css$/,use:[‘style-loader‘,‘css-loader‘]} //配置处理 .css文件的第三方loader规则 ] }
-
在main.js入口文件里面引入CSS文件即可成功使用
import ‘./css/index.css‘
4.2 loader处理URL地址
问题描述:
- 默认情况下,
Webpack
无法处理CSS
文件中的URL地址,无论是图片还是字体库,只要是URL,都处理不了
解决方法:
-
通过安装
url-loader file-loader
插件来对URL进行处理npm i url-loader file-loader -D
使用步骤
-
安装
url-loader file-loader
插件 -
在
webpack.config.js
文件里面配置URL的处理规则- 参数介绍
- limit:图片的大小,单位是byte,如果引用的图片大于给定的limit值,则不会被转为
base64
格式的字符串, 如果 图片小于给定的 limit 值,则会被转为base64
的字符串 - name:设置URL指定的路径名,默认会以hash值来命名(防止重名),可以通过如下方式通过hash值拼图片原始名的方式来达到同样的效果,且辨识度更高
//URL图片路径的匹配规则 {test:/\.(jpg|jepg|png|gif)$/,use:‘url-loader?limit=1000&name=[hash:8]-[name].[ext]‘}, //字体图标的匹配规则 {test:/\.(eot|svg|woff|woff2|ttf)$/,use:‘url-loader‘}
4.3 拓展:Webpack处理第三方文件类型的过程
- 先校验文件类型,如果是js文件直接打包
- 如果非js文件,拿到后缀名,去webpack.config.js里面找对应匹配规则
- 找到则调用规则打包,否则报错
- rules的use规则数组从右到左调用,会将后面调用完毕的处理结果交给前面的规则继续处理
- 调用完毕之后会将处理结果直接交给Webpack进行打包合并,最终输出到bundle.js中去
示例:
- 先使用
css-loader
插件进行处理,将处理结果交给style-loader
继续处理
module: { rules: [ { test: /\.css$/, use: [‘style-loader‘, ‘css-loader‘] } ] }
4.4 总结
-
由于
Webpack
默认只能打包处理Js
类型的文件,无法处理其它的非Js
类型的文件,所以如果要处理非Js
类型的文件,我们需要手动安装一些合适的第三方loader
加载器。 -
安装并使用第三方加载器的方法为以下几步
- 在命令行安装相对应的loader加载器(注意一般的loader加载器都有相对应的依赖模块,依赖模块同样也需要安装,否则程序会运行报错)
- 安装完对应的loader加载器之后,在Webpack.config.js配置文件中配置相对应的匹配规则
//添加module对象 //在module对象里面添加rules数组 //在rules数组里面添加相对应文件的匹配规则 module:{ rules:[ {test:/\.css$/,use:[‘style-loader‘,‘css-loader‘]} ] }
- 如果有依赖的
CSS
或者其它文件,都以import
命令的方式在入口js
文件里面引入
-
完成以上步骤即可成功启用loader加载器
五.Babel的使用
在
Webpack
中,默认只能处理一部分ES6
语法,一些更高级的ES6
或者ES7
语法,Webpack
是处理不了的,这时候,就需要借助第三方loader,来帮助Webpack
处理这些高级的语法,当第三方loader把高级语法转为低级的语法之后,会把结果交给Webpack
去打包到bundle.js
中通过Babel,可以帮助我们将高级语法转换为低级的语法
5.1 安装
-
在Webpack中,可以运行如下两套命令,安装两套包,去安装Babel相关的loader功能
-
第一套包(类似于转换器):
cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
-
第二套包(提供转换关系):
cnpm i bebel-preset-env babel-preset-stage-0 -D
-
第一套包(类似于转换器):
5.2 配置
-
在Webpack.config.js配置文件中,在module节点下的rules数组中添加一个新的匹配规则
-
注意:
- 在配置的babel的loader规则的时候,一定要把node_modules目录通过exclude属性排除掉
- 如果不排除node_modules,则Babel会把node_modules中所有的第三方Js文件都打包编译,这些会消耗CPU,并且导致打包速度非常慢
- 哪怕最终babel把node_modules中的js全部转换完了,但是,项目也无法运行
{test:/\.js$/,use:‘babel-loader‘,exclude:/node_modules/}
-
在项目的根目录中,新建一个叫做
.babelrc
的Babel配置文件- 这个配置文件属于
json
格式,必须复核JSON
语法规范,不能写注释,字符串必须使用双引号 - 该配置文件可参照安装的两套包中的插件
- 第一套包里面有个
babel-plugin-transfrom
- 第二套包里面有
babel-preset-env,babel-preset-stage-0
{ "presets":["env","stage-0"], "plugins":["transform-runtime"] }
- 这个配置文件属于
-
完成以上配置,即可在项目中使用ES6语法,可以编写一个Class类,设置静态属性并打印它们来测试
六.在Webpack中使用Vue
传统的Vue使用方式
- 通过 script标签引入 vue.js文件
- 在body中声明一个盒子,并且为它设置一个id属性
- 在script标签中通过new Vue()创建一个VM实例
6.1 包的查找规则
当我们使用 import命令导入模块中的一个包的时候,它的查找规则是怎样的呢
- 找项目目录中有没有 node_modules 的文件夹
- 在node_modules文件夹中找到对应的包名
- 在package.json的配置文件里面,找到一个main属性【main属性指定了这个包在被加载的时候的入口文件】
接下来我们来试着导入一个vue包,并查看它对应的入口文件
-
安装
npm i vue -D
-
导入包
import Vue form ‘vue‘
-
查看 node_modules/vue/package.json中的main属性,看它的入口文件是哪个
"main": "dist/vue.runtime.common.js",
-
仔细回想一下,我们平时导入的是这个文件吗?显然不是,我们平时导入的是vue.js文件,那这里为什么会不一样呢?带着这个疑问,我们继续
6.2 尝试使用webpack的方式使用Vue
如下都是一些简单的代码模版,这里用代码说明,不做具体解释
//main.js 入口文件 //此处导入的是 vue.runtime.common.js import Vue from ‘vue‘ var login = { template:`Login success
` } var vm = new Vue({ el:‘#app‘, data () { return { msg:‘hello‘ } }, components:{ login } })
//index.html{{msg}}
运行,查看结果
- 发现报如下错误
- 您正在使用Vue的仅运行时版本,其中模板编译器不可用。 将模板预编译为渲染函数,或使用包含编译器的构建。
错误分析
- 以上错误告诉我们,正在使用的是vue运行时版本(即我们最开始看到的vue.runtime.common.js),编译器不可用
- 提供了两种解决方法
- 将模版预编译为渲染函数(使用render函数渲染模版)
- 使用包含编译器的构建(即完整的vue.js文件)
6.3 解决方法1
将模版预编译为渲染函数(使用render函数渲染模版)
渲染函数的使用,详情请看这里:Render函数的使用
通过尝试,发现把组件写在入口文件里面并不能达到目标,于是,我们抽取vue模版,通过vue文件来创建模版
-
创建 login.vue文件
这是通过 vue 文件创建的模版
-
安装加载.vue文件的插件,并配置webpack.config.js
cnpm i vue-loader vue-template-compiler -D
//webpack.config.js const vueLoaderPlugin =require(‘vue-loader/lib/plugin‘) plugins:[ new vueLoaderPlugin() ], module:{ rules:[ {test:/\.vue$/,use:‘vue-loader‘}, //处理.vue文件 ] },
-
在入口文件里面引入 login.vue,并使用render函数渲染
import Vue from ‘vue‘ import login from ‘./login.vue‘ var vm = new Vue({ el:‘#app‘, data () { return { msg:‘hello‘ } }, // render:function(createElement){ // return createElement(login) // }, //简写 render:c => c(login) })
-
再次运行,发现运行成功
6.4 解决方法2
用包含编译器的构建(即完整的vue.js文件)
修改原始导入的vue.runtime.common.js,将引入的包修改为vue.js
- 有如下几种方法都可以修改vue.js的引入路径(推荐第三种方式)
- 修改node_modules中的vue的package.json文件夹的main属性,将其指定为vue.js
- 引入的时候修改引入路径为
import Vue from ‘../node_modules/vue/dist/vue.js‘
- 还是按照原先的引入方法
import Vue form ‘vue‘
,但是需要在 webpack.config.js文件中增加如下配置
resolve:{ alias:{ "vue$":"vue/dist/vue.js" } }
- 完成以上设置之后,项目即可正常运行
6.5 总结
-
在Webpack中使用 Vue
- 安装 Vue 的包:
npm i vue -S
- 由于在Webpack中,推荐使用 .vue 这个组件 模版定义组件,所以需要安装能解析这种文件的loader:
npm i vue-loader vue-template-complier -D
- 在 main.js 中导入 Vue模块:
Import Vue from ‘vue‘
- 定义一个 .vue结尾的文件组件,该文件包含三个部分组成,template,script,style
- 在 main.js中使用
Import login from ‘./login.vue‘
命令导入这个组件 - 创建 vm实例,VM实例中使用渲染函数render来渲染页面
- 在页面中创建一个 id为app的div元素,作为VM要控制的区域
- 安装 Vue 的包:
七.在Webpack中使用 Vue-Router
7.1 基本使用
-
安装 vue-router依赖模块
npm i vue -S npm i vue-router -S
-
在main.js中引入Vue 和 vue-router模块,并显示的调用vue-router
import Vue from ‘vue‘ import VueRouter from ‘vue-router‘ Vue.use(VueRouter)
-
在src目录下创建App.vue主页面模版文件
APP组件
-
在 index文件中 创建一个 id 属性等于
app
的div元素 -
在
src
目录下面新建文件夹,并在新建的文件夹下面创建login.vue
和register.vue
文件 -
将
login.vue
和register.vue
组件应用到app
主页模板上面去APP组件
Login Register -
在
main.js
文件中配置路由,创建VM
实例,挂载app
import Vue from ‘vue‘ import VueRouter from ‘vue-router‘ Vue.use(VueRouter) import app from ‘./App.vue‘ import login from ‘./comp1/login.vue‘ import register from ‘./comp1/register.vue‘ var router = new VueRouter({ routes:[ {path:‘/login‘,component:login}, {path:‘/register‘,component:register} ] }) var vm = new Vue({ el:‘#app‘, render: c => c(app), router })
-
完成以上配置,启动项目即可
7.2 路由嵌套
在Webpack中使用路由的嵌套和在Vue中使用路由的嵌套基本类似
-
在路由基本使用的前提下,创建两个新的组件 account,money
-
在login组件中添加这两个组件的 ,并添加容器
Login success
Account Money -
在路由配置中给login添加children属性,配置account,money路由即可
var router = new VueRouter({ routes:[ {path:‘/login‘,component:login,children:[ {path:‘account‘,component:account}, {path:‘money‘,component:money}, ]}, {path:‘/register‘,component:register} ] })
7.3 抽离路由模块
为避免入口文件 main.js里面的内容过多,我们推荐使用抽离路由的方式,为router单独创建一个文件夹
-
在src目录下创建 router.js
import VueRouter from ‘vue-router‘ import login from ‘./comp1/login.vue‘ import register from ‘./comp1/register.vue‘ import account from ‘./comp2/account.vue‘ import money from ‘./comp2/money.vue‘ var router = new VueRouter({ routes:[ {path:‘/login‘,component:login,children:[ {path:‘account‘,component:account}, {path:‘money‘,component:money}, ]}, {path:‘/register‘,component:register} ] }) export default router
-
在main.js入口文件中,引入router.js暴露router对象,挂载到app即可
import Vue from ‘vue‘ import VueRouter from ‘vue-router‘ Vue.use(VueRouter) import app from ‘./App.vue‘ import router from ‘./router.js‘ var vm = new Vue({ el:‘#app‘, render: c => c(app), router })
7.4 组件中style标签lang属性和scoped属性
scoped属性:
- 在 .vue文件中,可通过style标签设置组件的样式,但是默认情况下,设置的样式会被渲染到全局
- 通过在 style标签中添加 scoped 属性,可以控制该样式只在该组件里面生效(建议使用)
lang属性:
-
在 .vue文件中,默认只能使用普通的 css样式语法,如果想要使用 scss,或者 less语法样式,需要给style标签设置 lang属性,并且指定具体的使用语法
复制代码
...未完待续,持续更新