webpack教程
参考:https://blog.csdn.net/qq_43682422/article/details/124054740
1 简介
在webpack官网有个图片,形象的展示了webpack的作用
webpack是一个模块化打包工具,其主要作用是对我们的项目进行文件归类、处理各个js之间的依赖、合并等操作,减少输出文件数量和大小,这样可以减少网站的请求数量和请求大小;
其次,webpack可以处理浏览器对ES5/ES6的兼容问题(通过将ES6代码编译为js代码)
2 全局安装webpack
npm i webpack -g
3 对项目文件进行打包
暂时把命令贴出来,具体js代码可以忽略不看
webpack ./src/main.js --output-filename bundle.js --output-path ./dist
这里,我们对main.js进行打包,设置其输出为/dist/bundle.js
4 实例
下面我们结合一个实例,说明为什么我们需要webpack,以及帮助我们理解webpack的原理
我们编写一个页面,这个页面中有10个li元素,我们希望间隔行变色显示
我们希望使用npm进行依赖包的管理,在项目根目录下执行 npm init 生成package.json
我们需要jquery包,我们执行 npm i jquery -S 进行依赖的安装
具体项目结构如下图所示
index.html
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> </head> <body> <ul> <li>这是第1个li</li> <li>这是第2个li</li> <li>这是第3个li</li> <li>这是第4个li</li> <li>这是第5个li</li> <li>这是第6个li</li> <li>这是第7个li</li> <li>这是第8个li</li> <li>这是第9个li</li> <li>这是第10个li</li> </ul> </body> </html>
main.js
import $ from 'jquery' $(function() { $('li:odd').css('backgroundColor', 'lightblue') $('li:odd').css('backgroundColor', function(){ return '#' + 'D97634' }) })
然后我们在index.html中引入main.js,查看能否正确执行这个js
<script src="./main.js"></script>
我们会收到一个报错,这个报错的意思是,浏览器不认识ES6语法import
Uncaught SyntaxError: Cannot use import statement outside a module (at main.js:1:1)
因此,我们必须使用webpack对main.js进行处理,处理方法为
webpack ./src/main.js --output-filename bundle.js --output-path ./dist
然后,我们会发现,dist路径下面多了一个bundle.js文件
然后,我们修改index.html中引入的js,改为引入bundle.js
<script src="../dist/bundle.js"></script>
然后,我们看到,效果出来了
5 webpack的功能
经过上面的例子,我们总结一下webpack的功能:
- webpack能够处理js文件的互相依赖关系
- webpack能够处理js的兼容问题,把高级的浏览器不识别的语法转化为低级的浏览器能够正常识别的语法
6 webpack.config.js
上例中,我们使用webpack打包时,需要带有文件名、输出目录、输出文件名等一大堆参数,我们希望通过配置文件,把需要打包的文件、输出路径、输出文件名配置到配置文件中,则我们就可以在项目根路径下创建webpack.config.js来达到目的
const path = require('path') //nodejs的语法,这个配置文件其实就是一个js文件,通过Node中的模块操作,向外暴露了一个配置对象 module.exports = { entry: path.join(__dirname, './src/main.js'),//入口,表示要使用webpack打包哪个文件 output: { //输出文件相关配置 path: path.join(__dirname, './dist'), //指定打包好的文件输出到哪个目录里面去 filename: 'bundle.js' //指定输出文件的名称 }, mode: 'development', devServer: { static: { directory: __dirname }, open: true } }
然后,我们只需执行 webpack 命令,就会实现打包目的。
当我们在命令行输出webpack命令后,做了如下几步
- 首先,webpack发现,我们并没有通过命令的形式给它指定入口和出口
- webpack就会去项目根路径下找一个叫做webpack.config.js的配置文件
- 当找到配置文件后,webpack会去解析执行这个配置文件,当解析执行完配置文件后,就得到了配置文件中导出的配置对象
- 当webpack拿到了配置对象后,就拿到了配置文件中的入口和出口,然后进行打包操作
7 webpack-dev-server
上例中,我们修改玩代码后,需要手动执行webpack进行打包操作,我们希望修改完代码后,自动实现打包功能,我们需要借助于webpack-dev-server这个工具来实现。
安装方法:
npm -i webpack-dev-server -D
安装完毕后,这个工具的用法和webpack命令的用法完全一样,我们使用一下看看效果
不巧,报错了,这个报错是因为,我们powershell中不能执行本地安装的命令,只能执行全局-g安装的名令
解决方法是:我们在package.json中配置script脚本
"scripts": { "dev": "webpack-dev-server" },
然后我么执行 npm run dev
浏览器会自动打开localhost:8080
我们看到输出信息中,webpack-dev-server为我们起了一个服务localhost:8080。此后每次我们修改了代码,进行保存,webpack-dev-server将会自动为我们进行打包编译,并且会自动刷新浏览器。
但是需要注意的是:webpack-dev-server帮我们打包生成的bundle.js文件输出到了项目根目录下,但是并没有存放到实际的物理磁盘上,而是直接托管到了电脑的内存中,所以我们在项目的根目录下根本找不到bundle.js文件。
这里我们需要修改index.html中script标签,引用项目根路径下的bundle.js, <script src="/bundle.js"></script> 。
8 html-webpack-plugin
上面,我们已经把bundle.js放到了内存中,我们可不可以把html也放到内存中?方法是有的,使用html-webpack-plugin插件就可以
安装
npm i html-webpack-plugin -D
在webpack.config.js中配置
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { ...省略 plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, './src/index.html'), filename: 'index.html' //指定生成的页面的名称 }) ] }
然后我们重新运行npm run dev查看页面并查看其源码,如下所示
<html lang="en"> <head> <title>Document</title> <script src="/bundle.js"></script> <script defer src="bundle.js"></script></head> <body> <ul> <li>这是第1个li</li> <li>这是第2个li</li> </ul> </body> </html>
我们看到,内存中生成的html比我们自己的html多了如下代码
<script defer src="bundle.js"></script>
也就是说,我们引入了html-webpack-plugin之后,我们不需要再手动处理bundle.js的引用路径,因为这个插件已经帮我们自动创建了一个合适的script并且引用了正确的路径。
9 webpack处理css
为什么css也需要webpack进行打包处理呢?其实我们完全可以在index.html中直接引入css,但是这里将会多一次请求css的动作。我们为了尽量减少二次请求,我们把css也纳入webpack的处理范围,也就是说,不论什么文件我们都在main.js中进行处理,index.html只引入一次main.js就可以了
webpack默认只能打包处理js文件,无法处理其他非js类型的文件
如果要处理非js类型的文件,我们需要手动安装一些合适的第三方loader加载器
安装
npm i style-loader css-loader -D --registry https://registry.npm.taobao.org
webpack.config.js配置文件中
module.exports = { ...省略 module: { //这个节点用于配置所有的第三方模块加载器 rules: [ //匹配规则 { test: /\.css$/, use: ['style-loader', 'css-loader'] } //配置处理.css文件的第三方loader规则 ] } }
css/index.css
li { list-style: none; }
main.js中引入css
import './css/index.css'
然后,我们再运行npm run dev,查看效果
我们看一看页面源码,只引入了bundle.js,没有其他css的引入。
<html lang="en"> <head> <title>Document</title> <!-- <script src="/bundle.js"></script> --> <script defer src="bundle.js"></script></head> <body> <ul> <li>这是第1个li</li> <li>这是第2个li</li> <li>这是第3个li</li> <li>这是第4个li</li> <li>这是第5个li</li> <li>这是第6个li</li> <li>这是第7个li</li> <li>这是第8个li</li> <li>这是第9个li</li> <li>这是第10个li</li> </ul> </body> </html>
webpack处理第三方文件类型的过程
- 发现这个要处理的文件不是js文件,然后就去配置文件中查找有没有对应的第三方loader规则
- 如果能找到对应的第三方规则,就会调用对应的loader处理这个文件类型
- 在调用loader的时候,是从后往前调用的,当最后一个loader调用完毕,会把处理的结果直接交给webpack进行打包合并,最终输出到bundle.js中去
10 webpack中配置babel
我们在main.js中使用ES6语法定义一个类
class Person { static info = { name: 'szj', age: 33 } } console.log(Person.info)
然后,我们npm run dev运行,将会报错,原因是webpack无法解析static关键字。
在webpack中,默认只能处理一部分es6的新语法,一些更高级的ES6语法webpack是处理不了的,这时候需要借助第三方loader来帮助webpack处理这些高级的语法,当第三方loader把高级语法转为低级的语法后,会把结果交给webpack去打包到bundle.js中。
通过babel,可以帮我们将高级的语法转换为低级的语法。
1 在webpack中可以运行如下命令,去安装babel相关的loader功能
npm install -D babel-loader @babel/core @babel/preset-env
2 然后再webpack的配置文件中,在module节点的下rules数组中,添加一个新的匹配规则
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/}
在配置babel的loader规则的时候,必须把node_modules目录,通过exclude选项排除掉
3 在项目的根目录中,新建一个叫做.babelrc的配置文件
{ "presets": [ [ "@babel/preset-env", // 也可以简写为 “@babel/env” { "targets": { "chrome": "68", "ie": "11" } } ] ] }
然后,我们npm run dev启动后,将不会报错,并且打印出console日志
11 在webpack构建的项目中使用vue
复习:
11.1 尝试使用vue
import Vue from 'vue' var vm = new Vue({ el: '#app', data: { msg: '123' } })
index.html
<html lang="en"> <head> <title>Document</title> </head> <body> <p><div id="app">{{ msg }}</div></p>p> </body> </html>
然后,我们运行npm run dev
发现页面报错:
11.2 问题原因
回顾包的查找规则
- 找项目根路径下有没有node_modules的文件夹
- 在node_modules中根据包名找对应的vue文件夹
- 在vue文件夹中,找package.json包配置文件
- 在package.json文件中查找一个main属性(main属性指定了这个包在被加载时候的入口文件)
这个文件是阉割版的vuejs,完整版的是本目录下的vue.js
11.3 问题解决
11.4 另一种方法
上例中,我们导入vue包的方式是
resolve: { alias: { //修改vue被导入时候的包的路径 'vue$': 'vue/dist/vue.js' } }
main.js中我们还是使用 import Vue from 'vue' 方式
这种方式更简便,也更符合我们的习惯
11.5 使用.vue定义组件
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build. (found in <Root>)
那我们能否使用vue.runtime.esm.js的情况下避免出现这种报错呢?
答案是可以的,这里我们引入一个新文件格式.vue,我们使用.vue定义组件。
首先,我们安装vue-loader和vue-template-compiler。他们的作用是将.vue文件中的<template><script><style>编译为统一的js代码,作为一个模板供Vue实例使用。(这里我们限定了版本号,用最新版本可能会报一些奇怪的错误,因为暂时无法解决,所以这里限定一下版本号)
npm i vue-loader@15.10.0 vue-template-compiler@2.6.10 -D --registry https://registry.npm.taobao.org
然后我们创建login.vue组件
<template> <div> <h3>这是登陆组件,使用.vue文件定义出来的</h3> </div> </template> <script></script> <style></style>
main.js,其中导入login.vue组件,并且用render方式渲染。(不能用components方式添加组件,会报错)
import Vue from 'vue' import login from './login.vue' var vm = new Vue({ el: '#app', data: { msg: '123' }, // components: { // login // } render: function(createElements) { return createElements(login) } })
(注:render函数可以使用箭头函数简化为: render: c => c(login) )
然后我们配置webpack.config.js。我们注释掉alias,添加VueLoaderPlugin和处理.vue文件的loader
const VueLoaderPlugin = require('vue-loader/lib/plugin') plugins: [ new VueLoaderPlugin() ], module: { rules: [ { test: /\.vue$/, use: 'vue-loader'} // 处理.vue文件的loader ] }, resolve: { alias: { //修改vue被导入时候的包的路径 // 'vue$': 'vue/dist/vue.js' } }
然后我们在index.html中使用定义的login组件
<body> <div id="app"> <p></p>{{ msg }}</p> <login></login> </div> </body>
效果如下
11.6 .vue中定义自己的数据和方法
在.vue文件中,我们可以定义data和methods
<template> <div> <h3>这是登陆组件,使用.vue文件定义出来的---{{ msg }}</h3> </div> </template> <script> export default { data() { return { msg: '123' } }, methods: { show() { console.log('调用了login.vue中的show方法') } } } </script> <style></style>
12 export和export default
12.1 export default
- 使用export default向外暴露的成员,可以使用任意变量来接收
- 在一个模块中只能使用export default 1次
新建test.js
export default { name: 'szj', age: 33 }
然后我们可以在main.js中导入
import m1 from './test' console.log(m1)
12.2 export
- 使用export向外暴露的成员,只能使用{}的形式来接收,这种形式叫做按需导出
- 使用export导出的成员,必须严格按照定义中的名称导出
- 使用export导出的成员,如果就想换个名称来接收可以使用as来起别名
test.js
export var title = '小星星'
import m1, { title } from './test' console.log(m1) console.log(title)
13 webpack中使用vue-router
安装vue-router依赖
npm i vue-router@3.0.2 -S --registry https://registry.npm.taobao.org
导入vue-router
import VueRouter from 'vue-router'
如果我们想在webpack中使用vue-router,必须手动安装VueRouter
Vue.use(VueRouter)
定义路由对象,其中Account.vue和GoodsList.vue是定义的两个组件
import account from './components/Account.vue' import goodslist from './components/GoodsList.vue' var router = new VueRouter({ routes: [ { path: '/account', component: account }, { path: '/goodslist', component: goodslist } ] })
再定义一个App.vue,放置<router-view>
<template> <div> <h3>这是App组件</h3> <router-link to="/account">Account</router-link> <router-link to="/goodslist">GoodsList</router-link> <router-view></router-view> </div> </template> <script> </script> <style> </style>
创建Vue实例
var vm = new Vue({ el: '#app', render: c => c(app), router })
效果
总结:
App这个组件是通过VM实例的render函数渲染出来了,render函数如果要渲染组件,只能放到el:'#app'所指定的元素中
Account和GoodsList组件是通过路由匹配监听到的,所以,这两个组件,只能展示到属于路由的<router-view>中去