目录
1.Webpack
webpack是一个现代的js应用的静态模块化打包工具,因此,webpack主要有2个内容:模块、打包;
通俗来讲,就是你开的产品,无法直接部署到服务器上,你需要经过webpack进行打包,进而可以被用户浏览器来识别,例如:
在ES6之前用到的AMD/CMD/ES6/CommonJS等模块解决方案、使用ES6的代码、模块之间的各种依赖等,webpack会把你的工程进行处理,自动处理模块之间的依赖关系,不仅仅是js文件,就连css、图片、json文件等资源都会被当做模块来使用和打包,最后处理成浏览器能够识别的代码;
Webpack中的各种资源打包合并成一个或多个包bundle、并且在打包过程中,还可以对资源进行处理,比如压缩图片,将scss转换成css、将ES6语法转换成ES5语法,将TypeScript转换成JavaScript等操作;
2.Webpack安装
webpack需要依赖node环境支持(node底层是基于CommonJS规范来实现的),node环境为了针对不同文件类型、不同语法提供正常的执行,就必须包含对应的各种依赖包,需要哪个依赖包就下载哪个依赖包,为了更好的管理node环境下的依赖包,我们需要安装npm工具,全程:node package manager;
下面是安装步骤:
①安装node.js,node.js自带npm管理工具;
从node.js官网下载node-v12.19.0-x64.msi,双击安装,安装完毕后,通过node -v查看版本号:
②安装webpack,这里选择安装3.6.0版本,指令为:npm install webpack@3.6.0 -g,安装成功后通过命令查看:webpack -version
③在电脑“系统环境变量”->“PATH”中配置node.js的安装路径;
④然后在IntelliJ IDEA中查看node.js以及NPM管理器:
3.Webpack基本使用
①新建src和dist目录
src:存放前端工程源文件的地方;
dist:打包后,包存放的路径;
②根据模块功能划分,编写不同的模块
utils模块,这里采用的是CommonJS的导出语法
function add(num1,num2) { return num1 + num2; } // 这里使用CommonJS的规范 module.exports = { add } |
infos模块,这里采用的是ES6的导出语法
// 这里使用的是ES6的语法 export let name = 'zhangsan'; export let age = 18; |
编写前端项目的入口js:main/index.js
// 这里使用CommonJS的规范 const {add} = require('./utils'); console.log(add(20, 30)); // 这里使用的是ES6的语法 import {name,age} from './infos'; console.log(name,age); |
③使用webpack对入口js进行打包
webpack ./src/main.js ./dist/bundle.js
由于webpack会自己处理main.js与utils.js、infos.js的依赖关系,因此,这里只需要写明入口js即可;
④使用打包好的js文件
在前端HTML文件中使用打包好的js文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="./dist/bundle.js"></script> </body> </html> |
⑤这里核心的思想是:基于webpack提供的强大功能,使得开发者可以按照模块去划分功能,不必再去纠结于js语法细节,节省开发者的精力
4.webpack.config.js
为什么会有webpack.config.js文件呢?
答:刚才我们演示了,如果我们想把main.js发布成bundle.js时,我们需要在使用webpack指令时,明确对应的参数,那么,我们是否可以针对特定的某项目,针对该项目编写特定的配置文件,从而简化对应的webpack指令,那么,往后一次轮退,针对不同项目编写特定的配置文件,从而简化测试、开发等流程,所以,这才出现了webpack.config.js文件;
webpack.config.js是node.js环境的专属配置文件,该文件命名就是这样,不得随意修改;
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js' } } |
这里的path属性,需要配置绝对路径,但问题在于如果其他项目复用webpack.config.js文件,需要修改该path属性,如果不小心忘记的话,还挺麻烦的,这里就导入了node.js的path模块;
先初始化node.js:npm init
初始化之后,安装默认模块所依赖的其他模块:npm install
基于该项目,安装局部node.js:npm install webpack@3.6.0
当你基于当前项目安装了局部node.js之后,项目的目录结构:
变成:
且package.json文件变成:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.6.0" } } |
5.package.json
当你为本项目进行npm init、npm install的时候,node.js会自动创建package.json的文件;
在该文件的dependencies节点,说明了该项目所依赖的webpack的版本信息(npm install webpack@3.6.0);
该文件一个主要工作就是配置npm与webpack命令的映射关系,随着项目推进,用到的webpack命令越来越多,指令也越来越复杂,为了简化指令,我们可以在package.json的scripts节点中配置npm与webpack指令的映射关系,例如:
①刚开始的时候:webpack ./src/main.js ./dist/bundle.js
②通过在项目中配置node.js的项目个性化配置文件webpack.config.js后,把①中的指令简化为:webpack
③通过安装局部node.js,然后通过package.json来配置node与webpack指令的映射关系,通过scripts来把node.build映射为webpack,因此,把②中指令等效为:npm run build
注意:webpack存在本地webpack与全局webpack,全局webpack可以理解为所有项目通用的部分,而本地webpack则是各个项目的个性化配置,当我运行npm run xxx时,会优先查找本地的package.json文件,如果没有找到xxx映射的node指令,则再去全局webpack查找;(只要是终端使用webpack就是全局的,但是寻找映射命令的时候则先去找项目本地的json文件)
Package.json中的scripts脚本在执行时,会按照如下顺序寻找命令对应的位置:
①寻找本地node/modules/.bin对应的命令;
②如果没有找到,则去全局的环境变量中寻找;
那该如何调用本地项目个性化的node.js,方式有2种:
①在项目的package.json中配置命令;
②cd /node_modules/下,再去使用npm命令;
6.loader
Webpack的工作主要有2个:
①模块依赖:根据入口文件,凡是入口文件依赖的模块,都会被一起打包;
②打包:根据入口文件,凡是入口文件依赖的模块,都会被一起打包;
特别是在打包的时候,可能会做如下工作:
css文件打包、图片打包、es6转es5、typescript转es5、scss/less转css、jsx/vue文件转js等等,这些工作都是由webpack的loader来完成的,下面是官网提供的各种loader:
使用loader,大致分为如下几个步骤:
①需要通过npm安装需要使用的loader,去官网查看loader:https://webpack.js.org/,https://webpack.js.org/loaders/
②在webpack.config.js的modules节点下进行配置;
6.1案例
这里以样式文件normal.css为例:
①打开官网,找到css-loader,通过npm install安装该loader,并且配置webpack.config.js;
npm uninstall css-loader
npm install --save-dev css-loader@2.0.2
②找到style-loader,按照官网的步骤进行安装:
npm uninstall style-loader
npm install --save-dev style-loader@0.23.1
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js' }, module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] } ] } } |
③在入口文件main.js中,添加对该css文件的依赖;
// 这里使用CommonJS的规范 const {add} = require('./js/utils'); console.log(add(20, 30)); // 这里使用的是ES6的语法 import {name,age} from './js/infos'; console.log(name,age); // 依赖css文件 require('./css/normal.css'); |
④通过npm run build,把js和css打包到bundle.js中即可生效;
6.2.less/scss/stylus样式文件
这种类型的文件,其处理方式与CSS文件类型,大致步骤如下:
①按照官网给出的指令,进行安装loader:
D:\Vue_Node_Space\Node>
npm install less@3.9.0 less-loader@4.1.0 --save-dev
其中less工具负责转化,而less-loader负责加载
②查看package.json,查看安装效果
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.6.0" }, "devDependencies": { "css-loader": "^2.0.2", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1" } } |
③查看webpack.config.js,查看配置
module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ {loader: 'style-loader'},//ceates style modes from js strings {loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] } ] } |
④编写LESS文件
@fontSize: 50px; @fontColor: orange; body { font-size: @fontSize; color: @fontColor; } |
⑤在入口文件main.js中,添加对该LESS文件的依赖
// 依赖less文件 require('./css/special.less'); document.writeln('<h2>测试less</h2>'); |
⑤打包
npm run build
6.3.加载图片
这里加载图片的时候,根据图片大小,分成2种加载方式:
①当加载的图片小于url-loader的limit(默认8192B)参数时,url-loader负责把图片转成base64字符串,然后HTML加载完base64字符串之后再去还原为图片,显示给用户;
②当加载的图片大于url-loader的limit(默认8192B)参数时,此刻webpack会用file-loader取代url-loader去打包图片,file-loader会把图片放到dist目录下,此刻,由于图片路径发生变化,导致CSS/LESS中url找不到,这里还需要在webpack.config.js中找到output节点,添加publicPath属性:publicPath: 'dist/';
下面是开发步骤:
①分别按照url-loader与file-loader:
npm install url-loader@1.1.2 --save-dev
npm install file-loader@3.0.1 --save-dev
②打开package.json查看安装效果:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.6.0" }, "devDependencies": { "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2" } } |
③在src/img/下放置图片,然后修改CSS样式文件:
body { /*background-color: green;*/ background: url("../img/1508047316483.jpeg"); font-size: 50px; } |
④修改webpack.config.js中的limit,先把图片用url-loader来打包
{ test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 1976320,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] } |
⑤在入口文件main.js中引用该CSS;
⑥打包,测试,查看效果
⑦修改webpack.config.js中的limit,改为8192,即8KB,换成file-loader来打包:
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', publicPath: 'dist/' }, module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] } ] } } |
⑧修改CSS文件样式加载的图片
body { /*background-color: green;*/ background: url("../img/che.jpeg"); font-size: 50px; } |
⑨重新打包,查看效果:
注意:
当图片大于limit限制时,name属性才生效,否则小于limit时,图片就是一个base64字符串,而字符串是没有名字的;
6.4.ES6转ES5
随着时间流逝,支持ES6语法的浏览器会越来越多,但为了兼容性,还是做一下吧:
①下载babel-loader:
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
其中babel-core表示核心,babel-preset-es2015表示是ES2015配置
②查看package.json,查看安装效果
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2" } } |
③配置webpack.config.js文件
{ test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } } |
④打包,npm run build
6.5.配置Vue
其配置过程与其他loader类似,下面一一列举:
①安装Vue
npm install vue@2.5.21 --save
npm install vue --save
②查看package.json,查看安装效果:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2" } } |
③导入Vue,编写Vue代码:
// 使用Vue进行开发 import Vue from 'vue'; const app = new Vue({ el: '#app', data: { message: '学习导入Vue' } }); |
在HTML文件中,定义app对应的标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{message}}</h2> </div> <script src="./dist/bundle.js"></script> </body> </html> |
④由于Vue打包时,有2个版本可以选择:
runtime-only:只包含运行时功能,无法编译任何template
runtime-compiler:包含运行时和编译时功能,可以编译template
通过配置webpack.config.js来配置runtime-compiler版本:
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' } }, module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } } ] } } |
⑤打包npm run build,查看效果
7.el与template区别
template里面编写HTML代码,然后编译的时候,这部分HTML代码会把el绑定的标签替换掉,例如:
const app = new Vue({ el: '#app', template: ` <div> <h2>{{message}}</h2> <button @click="btnClick"></button> </div> `}); 下面是HTML代码: <div id="app"> </div> |
最终展示效果:
之前id=app的<div/>已经被完全替换了
7.1.模块抽离过程1:template阶段
为什么会有template,或者说,template能改善什么?
答:既然template能完全替换el绑定的HTML标签,那么未来整个前段项目中,只存在一个HTML文件即可,且HTML文件内部大量存在类似于<div id="app"></div>代码,而里面的具体内容则交给Vue实例来完成;
7.2.模块抽离过程2:自定义组件阶段
随着大量的HTML代码嵌入到Vue实例的template中,会导致Vue实例的代码量大增,此时,我们可以利用之前学到的自定义组件对Vue实例的template等属性进行剥离,这样既可以通过自定义组件的形式来剥离业务逻辑,同时利用Vue编译的功能,用自定义组件来替换到template中的<App/>标签,然后再使用template特性去替换el绑定的标签,也能达成同样的效果:
// 使用Vue进行开发 import Vue from 'vue';
// import App from './vue/app'; // 自定义组件 const App = { template: ` <div> <button @click="btnClick"><h2>{{message}}</h2></button> </div> `, data(){ return { message: '学习导入Vue' } }, methods: { btnClick(){ console.log('template button click'); } } };
const app = new Vue({ el: '#app', template: `<App/>`, components:{ App } }); |
7.3.模块抽离过程3:自定义组件封装阶段
既然我们的template、data、methods可以封装到自定义组件中,那么随着业务增大,大量的自定义组件、与Vue实例的代码,合并在同一个js文件中,会导致不好维护;
因此,我们新建一个js文件,然后把自定义组件的代码复制到该js文件中,然后在Vue实例中通过import来使用即可,这样既可以实现模块封装的效果:
App.js
export default { template: ` <div> <button @click="btnClick"><h2>{{message}}</h2></button> </div> `, data(){ return { message: '学习导入Vue' } }, methods: { btnClick(){ console.log('template button click'); } } } |
主入口文件main.js
// 使用Vue进行开发 import Vue from 'vue';
// 自定义组件 // const App = { // template: ` // <div> // <button @click="btnClick"><h2>{{message}}</h2></button> // </div> // `, // data(){ // return { // message: '学习导入Vue' // } // }, // methods: { // btnClick(){ // console.log('template button click'); // } // } // };
// 导入App自定义组件 import App from "./vue/app";
const app = new Vue({ el: '#app', template: `<App/>`, components:{ App } }); |
这样,一个模块对应一个js文件,需要修改哪个模块,就打开哪个js文件;
7.4.模块抽离过程4:webpack封装阶段
虽然,我们把自定义组件封装到了一个js文件中,但我们需要在js文件中的对象里定义HTML代码、事件、样式等还是比较复杂,维护起来也是比较麻烦,特别是在JS中写HTML代码简直就是灾难,这里可以借用webpack的功能来解决这个问题:
①从Intellij idea市场下载vue.js插件,或者,从vue.js官网(https://plugins.jetbrains.com/plugin/9442-vue-js
/versions)下载插件,来选择从disk来安装;
②然后新建名字叫app2的Vue Componet文件,Vue Component文件模板内容如下:
<template>
</template>
<script> export default { name: "app" } </script>
<style scoped>
</style> |
③利用Vue.js已经提供好了的Vue Component模板,把我们自定义组件app.js对应的属性添加到app2.vue对应的标签里面:
<template> <div> <button @click="btnClick"><h2>{{message}}</h2></button> </div> </template>
<script> export default { data(){ return { message: '学习导入Vue' } }, methods: { btnClick(){ console.log('template button click'); } } } </script>
<style scoped>
</style> |
④安装vue-loader与vue-template-compiler
npm install vue-loader --save-dev
npm install vue-template-compiler --save-dev
⑤查看package.json,看看安装情况:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12" } } |
⑥发现v15版的vue-loader配置需要加个VueLoaderPlugin,因此,在配置webpack.config.js文件配置VueLoaderPlugin:
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin() ], module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
⑦基于app2.vue文件取代app.js文件:
// 使用Vue进行开发 import Vue from 'vue';
// 自定义组件 // const App = { // template: ` // <div> // <button @click="btnClick"><h2>{{message}}</h2></button> // </div> // `, // data(){ // return { // message: '学习导入Vue' // } // }, // methods: { // btnClick(){ // console.log('template button click'); // } // } // };
// 导入App自定义组件 // import App from "./vue/app";
// 导入Vue Compoent,注意,这里要么写app2.vue,要么配置extensions后,就直接写app2就行了 import App from './vue/app2';
const app = new Vue({ el: '#app', template: `<App/>`, components:{ App } }); |
⑧打包npm run build,查看效果
8.plugin使用
Plugin通常是用于对某个现有的架构进行扩展;Webpack中的插件,就是一堆webpack现有功能的各种扩展,比如打包优化、文件压缩等;
Loader主要用于转换某些类型的模块,本质是一个转换器;而plugin是插件,是对webpack本身的扩展,是一个扩展器;
Plugin使用步骤:
①通过npm安装所需要使用的plugins(因为有些webpack已经内置了插件,不需要安装);
②在webpack.config.js中的plugins节点中配置插件;
8.1BannerPlugin插件
该插件负责为打包的文件添加版权说明,属于webpack自带的插件,下面是webapck.config.js的配置内容;
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); const webpack = require('webpack'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin(), new webpack.BannerPlugin('学习版权插件BannerPlugin') ], module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
8.2HtmlWebpackPlugin插件
我们需要把所有的js、less、css等文件打包进dist文件夹中,这里面也包括前端工程的入口文件index.html;
HtmlWebpackPlugin插件可以为我们做下面的事情:
①自动生成一个index.html文件(可以指定模板来生成)
②将打包的js文件,自动通过<script/>标签插入道<body/>中;
由于不是webpack自带插件,需要我们通过命令来手动安装HtmlWebpackPlugin插件:npm install html-webpack-plugin@3.2.0 --save-dev,安装完毕后,查看package.json
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12" } } |
webpack.config.js文件中plugins节点内容如下:
①template表示根据什么模板来生成index.html;
②删除在output节点添加的publicPath属性,否则插入的<script/>标签中的src属性会出问题、还有图片加载的路径也会出问题;
下面是配置步骤:
①安装html-webpack-plugin插件,根据情况选择适合自己的版本:npm install html-webpack-plugin@3.2.0 --save-dev
②配置webpack.config.js,并且指定HTML模板:
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', // publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin(), new webpack.BannerPlugin('学习版权插件BannerPlugin'), new HtmlWebpackPlugin({ template: `index.html` // 根据当前webpack.config.js所在目录下,去查找一个名字叫index.html的文件 }) ], module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
③修改我们指定的HTML模板文件(根据webpack.config.js中指定的HTML文件)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <!-- 这里清除了<script/>标签,因为插件会自动添加该标签 --> </body> </html> |
④打包npm run build,这里插件自动添加了<script/>标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <script type="text/javascript" src="bundle.js"></script></body> </html> |
⑤打开浏览器,查看打包后的html文件
8.3.UglifyjsWebpackPlugin插件
在项目发布之前,我们必须对js等文件进行压缩处理,这里我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定为1.1.1,为了与CLI2保持一致
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
安装完毕后,查看package.json文件:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12" } } |
然后继续在webpack.config.js中使用该插件:
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const uglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', // publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin(), new webpack.BannerPlugin('学习版权插件BannerPlugin'), new HtmlWebpackPlugin({ template: `index.html` // 根据当前webpack.config.js所在目录下,去查找一个名字叫index.html的文件 }), new uglifyJsPlugin() ], module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
然后打包npm run build,查看js的压缩效果:
之前的效果:
压缩之后的效果:
注意:该插件会把注释、空格删掉,然后变量重命名,因此,webpack.BannerPlugin插件所做的版权声明也给去掉了;
9.webpack-dev-server
每次修改完文件,无论是哪个文件,都要打包npm run build,这样非常浪费时间,为了提高效率,webpack提供了本地服务器;
webpack提供了一个可选的本地开发服务器,这个本地服务器是基于node.js搭建的,内部使用express框架,可以实现我们想要的让浏览器自动刷新、显示我们修改后的结果;
①由于是一个单独的模块,在webpack中使用之前需要先安装:
npm install webpack-dev-server@2.9.3 --save-dev
②查看package.json,看看安装效果:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "dev": "webpack-dev-server --open" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12", "webpack-dev-server": "^2.9.3" } } |
感慨一下:
③配置webpack.config.js:devserver也可以作为webpack的一个选项,其本身有如下属性:
contentBase为哪一个文件夹提供本地服务,默认是根文件夹
port端口号,默认8080
inline页面实时刷新
historyApiFallback在SPA页面中,依赖HTML5的history模式
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const uglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_ path: path.resolve(__dirname,'dist'), // 文件名 filename:'bundle.js', // publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin(), new webpack.BannerPlugin('学习版权插件BannerPlugin'), new HtmlWebpackPlugin({ template: `index.html` // 根据当前webpack.config.js所在目录下,去查找一个名字叫index.html的文件 }), new uglifyJsPlugin() ], devServer: { contentBase: './dist', inline: true }, module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
④安装、配置完毕后,可以有2种方式来启动:
方法1:cd node_modules\.bin\webpack-dev-server目录下来启动,但这种方式比较繁琐;
方式2:配置package.json的scripts节点,针对webpack-dev-server做一个映射,映射为一个简单的指令,例如:
{ "name": "node", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "dev": "webpack-dev-server --open" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12", "webpack-dev-server": "^2.9.3" } } |
这里的--open参数表示自动直接打开浏览器
我这里采用方式2
⑤启动,测试:npm run dev
鉴于我们添加了--open参数,所以,当这个命令运行完毕之后,webpack会自动打开浏览器供我们查看结果;
8.1.总结
webpack-dev-server是把文件放在内存里面,等项目终结的时候,只需运行一次npm run build,然后发布dist文件夹即可;
当我们修改文件之后,浏览器会自动刷新,无法人工按F5;
9.配置文件分离
我们在开发的时候,特别是开发阶段,不需要UglifyjsWebpackPlugin来丑化JS,因为丑化之后调试成本太高、开发阶段用的是devServer,而到了run build打包阶段则不需要了,所以,UglifyjsWebpackPlugin用于打包、devServer用于开发一样,想把这2种类型的内容,从1个webpack.config.js中拆开;
我说一下大致思路:
①把webpack.config.js拆分成3个文件:base.config.js(无论是哪个环境,都公用的配置)、dev.config.js(开发环境专有配置)、prod.config.js(生产环境专有配置);
base.config.js
// 导入node.js的path包 // npm init 来为环境初始化node环境 // npm install 来安装依赖 // npm install webpack@3.6.0 --save-dev 安装本地webpack const path = require('path'); const vueLoaderPlugin = require('vue-loader/lib/plugin'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const uglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { // 定义入口 entry: './src/main.js', // 定义出口 output: { // 定义绝对路径,注意是2个_,这里的dirname指的是当前配置文件所在的目录,指向根目录下的/dist文件夹 path: path.resolve(__dirname,'../dist'), // 文件名 filename:'bundle.js', // publicPath: 'dist/' }, resolve: { // 别名,当我们install vue之后,会在node_modules下存在vue文件夹 // 通过别名,我们指向了node_modules/vue/dist/vue.esm.js文件 alias: { 'vue$': 'vue/dist/vue.esm.js' }, // 我不用继续在main.js中,针对vue文件都要添加.vue后缀了 extensions:['.js','.css','.vue','.less'] }, plugins:[ new vueLoaderPlugin(), new webpack.BannerPlugin('学习版权插件BannerPlugin'), new HtmlWebpackPlugin({ template: `index.html` // 根据当前webpack.config.js所在目录下,去查找一个名字叫index.html的文件 }), new uglifyJsPlugin() ], module: { rules: [ { test: /\.css$/i, // css-loader负责加载css文件,style-loader负责解析生效css,添加到DOM中 // 当使用多个loader时,从右向左依次加载loader use: ['style-loader', 'css-loader'] }, { test: /\.less$/, loader: [ //{loader: 'style-loader'},//ceates style modes from js strings //{loader: 'css-loader' },// translates css into commonjs {loader: 'less-loader' }// compiles less to css ] }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192,// 单位为B name: 'img/[name].[hash:8].[ext]' // img:文件要打包到的文件夹 // name:获取图片原来的名字,放在该位置,例如: name:'img/[name].[hash:8].[ext]' // hash:8:为了防止图片名称冲突,依然使用hash来命名,但我们只保留8位;原先是32位的hash值 // ext:使用图片原来的扩展名 } } ] }, { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] //['@babel/preset-env'] } } }, { test: /\.vue$/i, use: ['vue-loader'] } ] } } |
dev.config.js
const webpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = webpackMerge(baseConfig,{ devServer: { contentBase: './dist', inline: true } }); |
prod.config.js
const uglifyJsPlugin = require('uglifyjs-webpack-plugin'); const webpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = webpackMerge(baseConfig,{ plugins:[ new uglifyJsPlugin() ] }); |
②安装webpack-merge;
npm install webpack-merge@4.1.5 --save-dev
查看package.json
{ "name": "node", "version": "1.0.0", "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/prod.config.js", 这里配置打包发布时的配置文件 "dev": "webpack-dev-server --open --config ./build/dev.config.js" 这里配置dev命令加载的配置文件 }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.12", "webpack": "^3.6.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^1.1.2", "vue-loader": "^15.9.4", "vue-template-compiler": "^2.6.12", "webpack-dev-server": "^2.9.3", "webpack-merge": "^4.1.5" } } |
③通过webpack-merge,整合base.config.js与dev.config.js;参考上面的配置代码;
④通过webpack-merge,整合base.config.js与prod.config.js;参考上面的配置代码;
⑤配置base.config.js中关于打包配置文件的参数;参考上面的配置代码;
⑥配置base.config.js中关于发布路径的参数;参考上面的配置代码;
⑦主配置文件webpack.config.js可以删除了;