一、Webpack做爲前端構建工具的用途 文件在webpack01中
代碼轉換:如ts轉換爲js
文件優化:對文件進行壓縮
代碼分割
模塊合併
自動刷新
代碼校驗
自動發佈css
二、安裝webpack webpack-cli工具
命令:html
npm install webpack webpack-cli -D
裏面的核心概念:
入口 : 默認找的是src/index
出口 :自動打包出的文件是dist/main.js
loader
plugins:插件前端
根據運行環境不一樣,js的寫法也不相同,因一個js就是一個模塊。
esModule(瀏覽器環境) commonJS(Node環境)的不一樣:
esModule 引入模塊:import 暴露模塊:export
commonJS 引入模塊:require 暴露模塊:module.exportsnode
簡單打包
查看本地的npm版本webpack
npm -v
如果5.2.0+,即5.2以上版本,能夠直接npx webpack進行打包
這個命令:npx webpack
=> 這個命令是找到node_modules/.bin/webpack命令,內部會去webpack-cli,根據用戶本身的配置,進行解析,隨後執行打包操做。若沒有本身配置,就會找webpack默認的。(用戶配置的文件就是webpack.config.js)
能夠本身配置,package.json:git
"scripts":{ "dev":"webpack --mode developent", "build":"webpack --mode production" }
運行命令:
npm run dev / npm run build
執行打包es6
插件 plugin:
因webpack支持的插件是少的,因此須要本身手動添加安裝,及配置。使用到的文件是:webpack.config.js。
html-webpack-plugin 插件:將打包好的js自動引入html
安裝命令:github
npm install html-webpack-plugin -D
index.html是模板文件,想將打包好的js自動引入到index.html,不用本身再配置了,就須要在webpack-config.js中引入此插件,使用便可。(開發環境中使用。)
具體使用在webpack.config.js/plugins中設置(先引入,再使用)。 配置完,運行打包命令,就能夠將打包好的js直接引入到html文件中,web
clean-webpack-plugin 插件:清空上次打包好的文件(在這裏是dist)
安裝命令:ajax
npm install clean-webpack-plugin -D
在dist打包成功的文件夾下,可能有多個文件,爲避免混亂,能夠引入這個插件.
webpack-dev-server
建立本地服務器,自動從新構建,自動打開瀏覽器並刷新
安裝命令:
npm install webpack-dev-server -D
下載完後,須要在webpack.config.js文件中進行配置,具體配置:
// devServer自動構建並打開 devServer: {//在內存中打包,全部的目錄是在根目錄下 port: 7777, //端口 compress: true, //是否壓縮代碼 open: true, //是否自動打開瀏覽器 contentBase: "static", //啓動配置一個訪問的靜態資源文件 hot: true //自動刷新 },
配置完後,須要在package.json的scripts中,添加dev-server的命令
"scripts": { "build": "webpack --mode production", "dev": "webpack --mode development", "dev:server": "webpack-dev-server" },
多入口文件 多出口
<!-- 多個入口文件 多出口 --> entry: { index: "./src/index.js", other: "./src/other.js" }, output: { filename: '[name].js', //name表明上方的index/other path: path.resolve(__dirname, "dist") }
若想打包的時候有多個文件,同時引入不一樣的js
能夠定義數組,遍歷
let htmlPlugin = ['index', 'other'].map(chunkName => { return new HtmlWebpackPlugin({ template: `./${chunkName}.html`, filename: `${chunkName}.html`, chunks: [chunkName] }) })
最後用展開運算符執行…htmlPlugin放入plugins數組中 ,詳見最下面的配置代碼
loader 解析文件
直接寫樣式,而後在index.js引入index.css是不生效的,運行npm run dev:server會報錯。
解析CSS:css-loader style-loader
安裝命令:
npm install css-loader style-loader -D
安裝完後,去webpack.config.js中進行配置
module:{ relus:[ // 如有多個解析器,可使用數組形式[],單個用字符串形式便可。 { test: /\.css$/, // 值能夠是[] 數組形式/ {}對象形式 / ""字符串形式 use: ['style-loader', 'css-loader'] } ] }
解析less : less less-loader
安裝命令:
npm install less less-loader -D
安裝後,相似上面的css-loader,也要設置下config文件,若將less文件引入到css中,也須要修改配置文件,詳見webpack.config.js
解析sass :node-sass sass-loader
解析stylus : stylus stylus-loader
postcss-loader 插件:配置CSS3私有前綴
如用到CSS3的新屬性,瀏覽器會有不一樣的前綴,纔可生效,就用到這個postcss-loader(樣式處理工具)
會用到好比autoprefixer(處理私有前綴的插件)
在使用前,先安裝:
npm install postcss-loader autoprefixer -D
安裝後,須要新建一個文件名爲:postcss.config.js的文件,因會調用這個配置文件.
在裏面引入下載的處理私有前綴的文件。
以後,要在webpack.config.js中的css-loader位置,加上這個postcss-loader,加前綴。
在module.exports => module => rules 下寫上:
{ test: /\.css$/, use: [ 'style-loader',//在使用MiniCssExtractPlugin插件分離出css後,這裏的style-loader就不須要了 { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'less-loader' //加在less-loader前面 ] },
(能夠運行npm run dev,看打包好的index.js,搜索就能夠看到添加的CSS3屬性的前綴)
若想要覆蓋99.5%的瀏覽器,根目錄下新建一個文件".browserslistrc"
內容爲:cover:99.5%
mini-css-extract-plugin 插件:分離css
安裝命令:
npm install mini-css-extract-plugin -D
安裝後,要在webpack.config.js中引入,並使用。具體使用見webpack.config.js
引入:
而後在plugins使用:
new MiniCssExtractPlugin({ filename: 'css/main.css' //設置分離出的css的存放目錄及文件名 })
還須要在 配置解析器 module => relus中寫入:
{ test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'less-loader' ] },
就能夠分離css了(能夠npm run dev 查看,dist下會有一個css/main.css)
optimize-css-assets-webpack-plugin 插件:壓縮css
當項目上線,代碼會進行壓縮,若使用了MiniCssExtractPlugin插件分離css,打包的時候,js會自動壓縮,可是css不會,因此須要安裝單獨的插件來壓縮css文件
安裝命令:
npm install optimize-css-assets-webpack-plugin -D
安裝後,在webpack.config.js中引入:(使用大駝峯命名)
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
而後在與entry,output,module,plugins的同級,寫上:
// webpack中配置壓縮css,js的規定用法:
optimization: { minimizer: [//壓縮的css ,js new OptimizeCssAssetsWebpackPlugin() ] },
執行npm run build
就能夠看到css,被壓縮了。
可是,js此時就不會自動壓縮了。須要使用terser-webpack-plugin插件(覆蓋默認壓縮工具minimizer).
terser-webpack-plugin 插件 壓縮js
安裝命令:
npm install terser-webpack-plugin
不能放到開發環境下
而後根據壓縮css的使用方法使用便可。
引入:
const TerserJsPlugin = require('terser-webpack-plugin');
使用:
optimization: { minimizer: [//壓縮的css ,js new OptimizeCssAssetsWebpackPlugin(), new TerserJsPlugin() ] },
而後使用npm run build
就能夠看到,css和js都壓縮成功了。
圖片須要的解析器 webpack03中開始
file-loader:把圖片解析成一個文件傳進來
url-loader:把一些小圖片解析成base64插入到頁面中
安裝命令:
npm install file-loader url-loader -D
安裝後,在index.js中,引入一張圖片(已經下載好的),能夠寫以下代碼:
import url from './img1.jpg'; // let oImg = document.createElement('img'); let oImg = new Image(); oImg.src = url; document.body.appendChild(oImg);
在webpack.config.js中,須要在module中的rules配置flie-loader
{ test: /\.(jpg|jpeg|gif)$/, use: { loader: 'file-loader', options: { name: 'img/[name].[ext]' } } }
打包npm run dev
時,就能夠把這個圖片當作一個文件傳進去了
如果圖片不大的話,能夠選擇使用url-loader,轉換爲base64的格式
index.js中的引入不變,變化的是webpack.config.js中module=>rules的內容:
{ test:/\.(jpg|jpeg|gif)/, use:{ loader:'url-loader', //小於100kb的使用url-loader options:{ limit:100*1024, //100kb outputPath:'img', publicPath:'http://www.baidu.com/img' //這能夠寫網上的地址 } } }
對於圖標:iconfont
官網地址:https://www.iconfont.cn/?spm=a313x.7781069.1998910419.d4d0a486a
使用方法:
挑選使用的iconfont,將挑選好的iconfont,能夠新建一個項目,直接下載到本地(新建的項目是指在iconfont網中的添加項目,須要下載到本地)
把須要的文件(iconfont.css & iconfont.eot & iconfont.svg & iconfont.ttf & iconfont.woff & iconfont.woff2)放入到項目中src中
要在項目index.js中引入這個下載好的圖庫
import './icon/iconfont.css'; //引入iconfont的樣式文件 let i = document.createElement('i'); i.className = 'iconfont icon-agreement'; document.body.appendChild(i);
而後從新配置下webpack.config.js文件下的module=>relus:
{ test:/\.(eot|svg|ttf|woff|woff2)$/, use:'file-loader' }
從新npm run devServer 就能夠在頁面中看到這個iconfont了
將ES6,ES7轉換爲全部瀏覽器都支持的ES5
在webpack中,一個重要的模塊,此功能使用到的插件有:
@babel/core babel的核心模塊
babel-loader 解析js代碼,webpack和babel的橋樑
@babel/preset-env es6轉化爲es5插件的集合
安裝命令:
npm install @babel/core babel-loader @babel/preset-env -D
安裝完後,配置webpack.config.js中的module=>rules:
{ test: /\.js$/, use: 'babel-loader', include: path.resolve(__dirname, 'src'), //須要編譯的js文件的目錄 exclude: 'node_modules' //排除須要編譯js文件的目錄 }
運行打包npm run dev
,發現打包出的文件中仍是箭頭函數,因此須要配置babel的預設。
建立一個文件名爲:.babelrc,裏面的內容爲:
{ // preset 預設(插件的集合)從下往上 "presets":[ "@babel/preset-env" ], // plugins 一個個插件,從上往下 "plugins":[] }
再去執行npm run dev
,就會看到箭頭函數已經能夠解析成功了,可是有些ES6的方法,依然是不解析的,須要另外配置參數。
舉個栗子:
index.js中寫入以下代碼:
console.log('nihaoshijie'.includes('g'));
npm run dev
,在其中查找includes,這個方法仍是這樣寫的,沒有解析
因此須要修改.babelrc值:
{ // preset 預設(插件的集合)從下往上 "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage" //只轉化使用的api } ] ], // plugins 一個個插件,從上往下 "plugins": [] }
修改後,npm run dev
會提示要去下載core-js
會要求下載core-js@3 這個至關於以前用如今廢棄的@babel/pollyfill
這時根據提示下載:npm install --save core-js@3
而後在進行配置.babelrc的內容,修改成:
{ // preset 預設(插件的集合)從下往上 "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", //只轉化使用的api "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)功用是轉化es6中高版本的api(好比promise,async,await這種) } ] ], // plugins 一個個插件,從上往下 "plugins": [] }
再運行npm run dev
,在打包好的文件中,搜索includes,就能夠看到已經給轉化爲瀏覽器均可識別的es5方法
<!-- 裝飾器 --> 在index.js中寫一個裝飾器的例子: //草案 // 裝飾器 語法糖(吃起來甜,理解有些難) @fn class Son{ a = 1 } function fn(target) { target.flag = true; console.log(target); } let S = new Son(); console.log(Son.flag);
運行 npm run dev
,
會報錯,因不能識別,須要用到下列的包
@babel/plugin-proposal-class-properties 修飾類,還有一個@babel/plugin-proposal-decorators 修飾裝飾器的
如有一些草案的語法,須要修飾器,用到一些包:
安裝:npm install @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
安裝後,配置寫**.babelrc**文件內容爲:
{ // preset 預設(插件的集合)從下往上 "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", //只轉化使用的api "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)轉化es6中高版本的api } ] ], // plugins 一個個插件,從上往下 "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true //若爲false使用舊版本,用true使用最新的 } ], [ "@babel/plugin-proposal-class-properties", { "loose": true //解析類的屬性,默認爲false。若用true是使用表達式 } ] ] }
寫完後,運行:npm run dev:server
,就能夠識別,而且會有打印值。
/*index.js中打印到的值 function Son() { _classCallCheck(this, Son); this.a = 1; } true */
能夠在sum.js中也寫上一個方法,使用修飾器的例子:
class Person { @readonly first = 1; } function readonly(target, name, descriptor) { descriptor.writable = false; //不可更改 } let p = new Person(); p.first = 100; //sum.js中,因已經寫了不可更改,使用了修飾器修飾first //會獲得以下錯誤:Uncaught TypeError: Cannot assign to read only property 'first' of object '#<Person>'
@babel/plugin-transform-runtime @babel/runtime 優化js
每一個js文件中有方法,在打包時會出現_classCallCheck方法,10個這種文件就會有10個_classCallCheck,因此會有冗餘代碼。想要優化js,會用到: @babel/plugin-transform-runtime @babel/runtime兩個插件
安裝:
npm install @babel/plugin-transform-runtime @babel/runtime -D
安裝後,配置.babelrc:
{ // preset 預設(插件的集合)從下往上 "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", //只轉化使用的api "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)轉化es6中高版本的api } ] ], // plugins 一個個插件,從上往下 "plugins": [ "@babel/plugin-transform-runtime", //這是優化js,去除每次調用的封裝方法會使用helpers的方法,輕便,會自動查找@babel/runtime,故不須要引入 [ "@babel/plugin-proposal-decorators", { "legacy": true //若爲false使用舊版本,用true使用最新的 } ], [ "@babel/plugin-proposal-class-properties", { "loose": true //解析類的屬性,默認爲false。若用true是使用表達式 } ] ] }
當配置好後,運行npm run dev
,查看打包好的bundle.js,會發現,以前的_classCallCheck方法都變成了_babel_runtime_helpers_classCallCheck來轉換類,再也不是哪一個文件有類就封裝,並且bundle.js大小也發生變化。
若無這行"@babel/plugin-transform-runtime",bundle.js是89kib
有"@babel/plugin-transform-runtime"的話,bundle.js是12.1kib
跨域問題:
須要一個後臺返回數據,可使用express
安裝下:
npm install express -D
而後新建一個server.js文件,中間寫上可成功運行的代碼:
let express = require('express'); let app = express(); app.get('/user', function (req, res) { res.json({ name: 'zoe' }) }) app.listen(8000, function () { console.log('8000服務已啓動') })
前端的服務端口號是7777,後臺是8000,能夠在index.js中,發送ajax,把以前代碼都註釋
let xhr = new XMLHttpRequest(); xhr.open('get', '/api/user', true); //異步 xhr.onreadystatechange = function () { //監聽請求是否成功,成功打印數據 console.log(xhr.response); } xhr.send();
啓動服務:
npm run dev:server
此時頁面中會報錯:HTTP404: 找不到 - 服務器還沒有找到與請求的 URI (統一資源標識符)匹配的任何內容。(XHR)GET - http://localhost:7777/api/user,由於跨域了。
要想解決這個問題,須要在webpack.config.js中的devServer中,配置proxy,就是代理,來解決跨域,配置以下,直接在devServer中寫:
devServer: { port: 7777, hot: true, contentBase: "static", open: true, proxy: { //啓動代理,解決跨域 '/api': { //請求地址以api開頭 target: 'http://localhost:8000', //請求的服務器地址 secure: false, //若爲true,表示以https開頭 pathRewrite: { '^/api': '' }, //重寫請求的地址 changeOrigin: true, //把請求頭中的host改爲服務器的地址 } } }
修改完配置信息,從新npm run dev:server 就能夠得到數據
若不須要後端配合,前端本身也能夠實現:
devServer.before 提供在服務器內部全部其餘中間件以前執行自定義中間件的能力。這可用於定義自定義處理程序。將以上寫的proxy註釋,並添加以下內容:
before: function (app, server) { //表示啓動一個端口號爲7777服務 app.get('/api/user', function (req, res) { res.json({ custom: 'response' }); }); }
配置完以上代碼,就能夠重啓 npm run dev:server,
就可解決跨域問題。
以上是解決跨域的兩種方法,看後端是否有地址,有的話,直接配置,沒有能夠本身實現。
附一個webpack.config.js文件
// 配置文件 // 遵循node中的commonJS規範,引入文件用require const path = require('path'); // console.log(path.resolve(__dirname, "dist")); //E:\Users\zoe\Desktop\學習js\zhufeng\webpack01\dist ----path.resolve()將地址轉成絕對路徑 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //功用:每次打包清空文件夾 const HtmlWebpackPlugin = require('html-webpack-plugin'); //功用:將打包好的js文件自動引入到html中 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //插件功用:分離css的插件 const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); //插件功用:壓縮css const TerserJsPlugin = require('terser-webpack-plugin'); //插件功用:壓縮js let htmlPlugin = ['index', 'other'].map(chunkName => { return new HtmlWebpackPlugin({ template: `./${chunkName}.html`, filename: `${chunkName}.html`, chunks: [chunkName] }) }) module.exports = { // entry: "./src/index.js", //單個的入口文件 // output: { // // 文件指紋:hash(整個項目產生的hash) chunkHash(根據出口文件產生的hash) contentHash(根據內容產生的hash) // // 爲防止緩存,加個hash // filename: 'bundle.js', // path: path.resolve(__dirname, "dist") // }, // 多個入口文件 多出口 entry: { index: "./src/index.js", other: "./src/other.js" }, output: { filename: '[name].js', //name表明上方的index/other path: path.resolve(__dirname, "dist") }, // webpack中配置壓縮css,js的規定用法: optimization: { minimizer: [//壓縮的css ,js new OptimizeCssAssetsWebpackPlugin(), //壓縮Css new TerserJsPlugin() //壓縮的js ] }, // 配置解析器: module: { // 需先加載css- loader,再加載style-loader rules: [ // 這裏的順序是: 從下往上 從右往左,因此寫的時候要注意順序 // 使用對象{}寫法: // { // test: /\.css$/, // use: 'css-loader' // }, // { // test: /\.css$/, // use: 'style-loader', // enforce: 'post' // pre優先加載 post 最後加載。 // } // 如有多個解析器,可使用數組形式[],單個用字符串形式便可。 { test: /\.css$/, // 值能夠是[] 數組形式/ {}對象形式 / ""字符串形式 // 若在index.css中引入a.less,要配置以下: use: [ // 'style-loader',在使用MiniCssExtractPlugin插件分離出css後,會經過外鍊形式引入到頁面,因此這裏的style-loader就不須要了 { loader: MiniCssExtractPlugin.loader //寫完存放分離出css路徑後還須要在此處調用一下loader(插件用法) }, { loader: 'css-loader', options: { importLoaders: 2 //用後面的1個加載器來解析,如有2個就寫2 } }, 'postcss-loader', 'less-loader' ] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] //因是從右向左,能夠先寫右邊的,而後在往前面加 } ] }, // devServer自動構建並打開 devServer: {//在內存中打包,全部的目錄是在根目錄下 port: 7777, //端口 compress: true, //是否壓縮代碼 open: true, //是否自動打開瀏覽器 contentBase: "static", //啓動配置一個訪問的靜態資源文件 hot: true, //自動刷新 // proxy: { //啓動代理,解決跨域 // '/api': { //請求地址以api開頭 // target: 'http://localhost:8000', //請求的服務器地址 // secure: false, //若爲true,表示以https開頭 // pathRewrite: { '^/api': '' }, //重寫請求的地址 // changeOrigin: true, //把請求頭中的host改爲服務器的地址 // } // } before: function (app, server) { //表示啓動一個端口號爲7777服務,不須要後端配合就能夠解決跨域 app.get('/api/user', function (req, res) { res.json({ custom: 'response' }); }); } }, // 插件在這裏設置 plugins: [ // new CleanWebpackPlugin({ // cleanOnceBeforeBuildPatterns: ['cc/*', '!cc/a.js'] // }) //清空cc下的全部文件,除了cc/a.js new CleanWebpackPlugin(), //清空輸出的目錄 new MiniCssExtractPlugin({ filename: 'css/main.css' //設置分離出的css的存放目錄及文件名 }), // 多的話就須要封裝函數:直接展開定義的實例化例子 ...htmlPlugin // 如有少的html,能夠這麼寫 // new HtmlWebpackPlugin({ // // 打包好的js插入到哪一個文件就使用template字段 // template: "./index.html",//依賴的模板文件 // hash: true, //能夠加hash,在打包好的文件中引入的js中有hash值 // minify: { //打包的時候去掉一些東西 // removeAttributeQuotes: true,//刪除引號 // collapseWhitespace: true//刪除空格 // }, // filename: "index.html", //打包後的html文件名 // chunks: ['index'] //指定引入的入口文件是哪一個 // }), // new HtmlWebpackPlugin({ // // 打包好的js插入到哪一個文件就使用template字段 // template: "./other.html",//依賴的模板文件 // hash: true, //能夠加hash,在打包好的文件中引入的js中有hash值 // filename: "other.html", //打包後的html文件名 // chunks: ['other'] // }) ] }
相關網址:
webpack官網
上述demo地址:webpack01Demo
本文同步分享在 博客「zoepriselife316」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。