基於webpack4的react開發環境配置


一、基礎配置

1、init項目

mkdir react-webpack4-cook
cd react-webpack4-cook
mkdir src
mkdir dist
npm init -y
復制代碼

2、安裝webpack

yarn add webpack webpack-cli webpack-dev-server -D //webpack4把webpack拆分了 touch webpack.config.js // webpack.config.js初始化內容 module.exports = { mode: "development", entry: ["./src/index.js"], output: { // 輸出目錄 path: path.join(__dirname, "dist"), // 文件名稱 filename: "bundle.js" }, module:{}, plugins:[], devServer:{} } 復制代碼

3、安裝react並編寫代碼

這部分代碼篇幅過多,就是一些簡單的react和react-router的一些代碼編寫,可以去github上查看,這里只闡述基本功能

cd src 
cnpm i react react-router-dom -S
// 建立如下的文件目錄,並編寫安裝react和react-router並編寫react代碼如下 |-src │ index.js 主文件 ├───pages │ Count.jsx -- 實現了一個計數器的功能,點擊按鈕,會讓數字增加,按鈕會實時顯示在頁面上 │ Home.jsx -- 一個簡單的文字展示 └───router index.js -- 路由配置文件,兩個頁面分別對應兩個路由 count和 home 復制代碼

4、babel編譯ES6,JSX等

// @babel/core-babel核心模塊 @babel/preset-env-編譯ES6等 @babel/preset-react-轉換JSX cnpm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/preset-react -D // @babel/plugin-transform-runtime: 避免 polyfill 污染全局變量,減小打包體積 // @babel/polyfill: ES6 內置方法和函數轉化墊片 cnpm i @babel/polyfill @babel/runtime { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: "babel-loader" } ] } 復制代碼

新建.babelrc文件

{
  "presets": ["@babel/preset-env","@babel/preset-react"], "plugins": ["@babel/plugin-transform-runtime"] } 復制代碼

5、按需引入polyfill

在src下的index.js中全局引入 @babel/polyfill 並寫入 ES6 語法 ,但是這樣有一個缺點:

  1. 全局引入 @babel/polyfill 的這種方式可能會導入代碼中不需要的 polyfill,從而使打包體積更大

更改 .babelrc,只轉譯我們使用到的

npm install core-js@2 @babel/runtime-corejs2 -S { "presets": ["@babel/preset-env", { "useBuiltIns": "usage" }, "@babel/preset-react"], "plugins": ["@babel/plugin-transform-runtime"] } 將將全局引入這段代碼注釋掉 // import '@babel/polyfill' 復制代碼

這就配置好了按需引入。配置了按需引入 polyfill 后,用到es6以上的函數,babel會自動導入相關的polyfill,這樣能大大減少 打包編譯后的體積

5、插件 CleanWebpackPlugin

你經過多次打包后會發現,每次打包都會在dist目錄下邊生成一堆文件,但是上一次的打包的文件還在,我們需要每次打包時清除 dist 目錄下舊版本文件

cnpm install  clean-webpack-plugin -D
// 注意這個引入的坑,最新版的需要這樣引入,而不是直接const CleanWebpackPlugin const { CleanWebpackPlugin } = require('clean-webpack-plugin'); plugins: [ new CleanWebpackPlugin() ] 復制代碼

6、使用插件 HtmlWebpackPlugin

經過上一步的操作,index.html 也被清除了。因此我們將使用 HtmlWebpackPlugin插件,來生成 html, 並將每次打包的js自動插入到你的 index.html 里面去,而且它還可以基於你的某個 html 模板來創建最終的 index.html,也就是說可以指定模板哦

cnpm install html-webpack-plugin -D
// 創建template.html cd src touch template.html // 內容如下 <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>react-webpack4-cook</title> </head> <body> <div id="root"></div> </body> </html> // webpack.config.js做出更改 const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ filename: 'index.html', // 最終創建的文件名 template: path.join(__dirname, 'src/template.html') // 指定模板路徑 }) ] 復制代碼

7、使用 source-map,對devtool進行優化

webpack中devtool選項用來控制是否生成,以及如何生成 source map。簡言之,source map就是幫助我們定位到錯誤信息位置的文件。正確的配置source map,能夠提高開發效率,更快的定位到錯誤位置。

在webpack.config.js中選項mode下加上如下這句話:

devtool:"cheap-module-eval-source-map",// 開發環境配置 devtool:"cheap-module-source-map", // 線上生成配置 復制代碼

8、使用 WebpackDevServer

webpack-dev-server就是在本地為搭建了一個小型的靜態文件服務器,有實時重加載的功能,為將打包生成的資源提供了web服務

  devServer: {
    hot: true, contentBase: path.join(__dirname, "./dist"), host: "0.0.0.0", // 可以使用手機訪問 port: 8080, historyApiFallback: true, // 該選項的作用所有的404都連接到index.html proxy: { // 代理到后端的服務地址,會攔截所有以api開頭的請求地址 "/api": "http://localhost:3000" } } 復制代碼

9、使用 HotModuleReplacement (熱模塊替換HMR)

建立了開發環境本地服務器 后,當修改內容后,網頁會同步刷新,我們現在進入toCOunt頁面

  1. 點擊按鈕,將數字加到一個不為0的數,比如加到6

  2. 然后你可以在代碼中改變按鈕的文字,隨便改點東西,會發現,頁面刷新后,數字重新變為0

這顯然不是我們想要的,想要的是,能不能把頁面的狀態保存了,也就是更改了代碼后,頁面還是保存了數字為6的狀態,也就是實現局部更改,首先需要用到:HotModuleReplacementPlugin插件

devServer: {
    hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ], 復制代碼

完事之后,繼續更上邊的操作,點擊按鈕,數字增加,然后更改內容,發現還是沒有保存狀態。。。what?怎么辦

對@!這還沒完呢,接着往下看,我們還需要react-hot-loader這個插件

10、react-hot-loader記錄react頁面留存狀態state

我們繼續接着上邊的進行操作,分一下四步

cnpm i react-hot-loader -D
 
// 在主文件里這樣寫 import React from "react"; import ReactDOM from "react-dom"; import { AppContainer } from "react-hot-loader";-------------------1、首先引入AppContainre import { BrowserRouter } from "react-router-dom"; import Router from "./router"; /*初始化*/ renderWithHotReload(Router);-------------------2、初始化 /*熱更新*/ if (module.hot) {-------------------3、熱更新操作 module.hot.accept("./router/index.js", () => { const Router = require("./router/index.js").default; renderWithHotReload(Router); }); } function renderWithHotReload(Router) {-------------------4、定義渲染函數 ReactDOM.render( <AppContainer> <BrowserRouter> <Router /> </BrowserRouter> </AppContainer>, document.getElementById("app") ); } 復制代碼

好了,現在你再試試

11、編譯css和scss

cnpm install css-loader style-loader sass-loader node-sass -D
 
{
  test: /\.scss$/, use: [ "style-loader", // 創建style標簽,並將css添加進去 "css-loader", // 編譯css "sass-loader" // 編譯scss ] } 復制代碼

12、集成postcss

最關心的還是這有啥用啊?自動增加前綴, postcss-cssnext允許你使用未來的css特性,並做一些兼容處理

cnpm install  postcss-loader postcss-cssnext -D
 
{
    test: /\.scss$/, use: [ "style-loader", // 創建style標簽,並將css添加進去 "css-loader", // 編譯css "postcss-loader", "sass-loader" // 編譯scss ] } // 在剛才的home.scss 加上 transform: scale(2); 通過控制台查看,已經自動加上了前綴 復制代碼

13、處理圖片

cnpm i file-loader url-loader -D
 
file-loader 解決css等文件中引入圖片路徑的問題
url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit參數的時候還是使用file-loader 進行拷貝
{
    test: /\.(png|jpg|jpeg|gif|svg)/, use: { loader: 'url-loader', options: { outputPath: 'images/', // 圖片輸出的路徑 limit: 10 * 1024 } } } 復制代碼

14、處理字體

{
        test: /\.(eot|woff2?|ttf|svg)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } ] } 復制代碼

二、webpack優化

1、alias對文件路徑優化

  1. extension: 指定extension之后可以不用在require或是import的時候加文件擴展名,會依次嘗試添加擴展名進行匹配
  2. alias: 配置別名可以加快webpack查找模塊的速度
  resolve: {
    extension: ["", ".js", ".jsx"], alias: { "@": path.join(__dirname, "src"), pages: path.join(__dirname, "src/pages"), router: path.join(__dirname, "src/router") } }, 復制代碼

14、使用靜態資源路徑publicPath(CDN)

CDN通過將資源部署到世界各地,使得用戶可以就近訪問資源,加快訪問速度。要接入CDN,需要把網頁的靜態資源上傳到CDN服務上,在訪問這些資源時,使用CDN服務提供的URL。

output:{
 publicPatch: '//【cdn】.com', //指定存放JS文件的CDN地址 } 復制代碼

2、MiniCssExtractPlugin ,抽取 css 文件

如果不做配置,我們的css是直接打包進js里面的,我們希望能單獨生成css文件。 因為單獨生成css,css可以和js並行下載,提高頁面加載效率

cnpm install mini-css-extract-plugin -D
 
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); { test: /\.scss$/, use: [ // "style-loader", // b不再需要style-loader要已經分離處理 MiniCssExtractPlugin.loader, "css-loader", // 編譯css "postcss-loader", "sass-loader" // 編譯scss ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }) ] 復制代碼

3、代碼分割按需加載、提取公共代碼

為什么要實現按需加載?

我們現在看到,打包完后,所有頁面只生成了一個bundle.js,當我們首屏加載的時候,就會很慢。因為他也下載了別的頁面的js,也就是說,執行完畢之前,頁面是 完!全!空!白!的!。 如果每個頁面單獨打包自己的js,就可以在進入頁面時候再加載自己 的js,首屏加載就可以快很多

  optimization: {
    splitChunks: { chunks: "all", // 所有的 chunks 代碼公共的部分分離出來成為一個單獨的文件 }, }, 復制代碼

5、文件壓縮

webpack4只要在生產模式下, 代碼就會自動壓縮

mode:productioin
復制代碼

6、暴露全局變量

可以直接在全局使用$變量

 new webpack.ProvidePlugin({ $: 'jquery', // npm jQuery: 'jQuery' // 本地Js文件 }) 復制代碼

8、指定環境,定義環境變量

plugins: [
    new webpack.DefinePlugin({ 'process.env': { VUEP_BASE_URL: JSON.stringify('http://localhost:9000') } }), ] 復制代碼

9、css Tree Shaking

npm i glob-all purify-css purifycss-webpack --save-dev
 
const PurifyCSS = require('purifycss-webpack') const glob = require('glob-all') plugins:[ // 清除無用 css new PurifyCSS({ paths: glob.sync([ // 要做 CSS Tree Shaking 的路徑文件 path.resolve(__dirname, './src/*.html'), // 請注意,我們同樣需要對 html 文件進行 tree shaking path.resolve(__dirname, './src/*.js') ]) }) ] 復制代碼

10、js Tree Shaking

清除到代碼中無用的js代碼,只支持import方式引入,不支持commonjs的方式引入

只要mode是production就會生效,develpoment的tree shaking是不生效的,因為webpack為了方便你的調試

  optimization: {
    usedExports:true, } 復制代碼

11、DllPlugin插件打包第三方類庫

項目中引入了很多第三方庫,這些庫在很長的一段時間內,基本不會更新,打包的時候分開打包來提升打包速度,而DllPlugin動態鏈接庫插件,其原理就是把網頁依賴的基礎模塊抽離出來打包到dll文件中,當需要導入的模塊存在於某個dll中時,這個模塊不再被打包,而是去dll中獲取。

安裝jquery,並在入口文件引入。新建webpack.dll.config.js文件

/* * @desc 靜態公共資源打包配置 */ const path = require('path') const webpack = require('webpack') const src = path.resolve(process.cwd(), 'src'); // 源碼目錄 const evn = process.env.NODE_ENV == "production" ? "production" : "development"; module.exports = { mode: 'production', entry: { // 定義程序中打包公共文件的入口文件vendor.js jquery: ['jquery'] }, output: { path: path.resolve(__dirname, '..', 'dll'), filename: '[name].dll.js', library: '[name]_[hash]', libraryTarget: 'this' }, plugins: [ new webpack.DllPlugin({ // 定義程序中打包公共文件的入口文件vendor.js context: process.cwd(), // manifest.json文件的輸出位置 path: path.resolve(__dirname, '..', 'dll/[name]-manifest.json'), // 定義打包的公共vendor文件對外暴露的函數名 name: '[name]_[hash]' }) ] } 復制代碼

在package.json中添加

        "build:dll": "webpack --config ./build/webpack.dll.config.js", 復制代碼

運行

npm run build:dll
復制代碼

你會發現多了一個dll文件夾,里邊有dll.js文件,這樣我們就把我們的jquery這些已經單獨打包了,接下來怎么使用呢?

需要再安裝一個依賴 npm i add-asset-html-webpack-plugin,它會將我們打包后的 dll.js 文件注入到我們生成的 index.html 中.在 webpack.base.config.js 文件中進行更改。

 new AddAssetHtmlWebpackPlugin({
 filepath: path.resolve(__dirname, '../dll/jquery.dll.js') // 對應的 dll 文件路徑 }), new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, '..', 'dll/jquery-manifest.json') }) 復制代碼

好了,你可有吧new webpack.DllReferencePlugin這個插件注釋掉,打包試下,在放開打包試一下,我測試結果,注釋錢5689,注釋后,5302ms,才差了300ms?注意,我這里只有一個jquery包作為演示,要是你把很多個都抽離了出來呢???那豈不是很恐怖了。如果你看的有點迷迷糊糊,那推薦去線上看一下我的代碼吧,一看便知

12、使用happypack並發執行任務

運行在 Node.之上的Webpack是單線程模型的,也就是說Webpack需要一個一個地處理任務,不能同時處理多個任務。 Happy Pack 就能讓Webpack做到這一點,它將任務分解給多個子進程去並發執行,子進程處理完后再將結果發送給主進程。

cnpm i -D happypack
 
// webpack.config.js rules: [ { // cnpm i babel-loader @babel/core @babel/preset-env -D test: /\.jsx?$/, exclude: /node_modules/, use: [ { // 一個loader對應一個id loader: "happypack/loader?id=busongBabel" } ] } ] //在plugins中增加 plugins:[ new HappyPack({ // 用唯一的標識符id,來代表當前的HappyPack是用來處理一類特定的文件 id:'busongBabel', // 如何處理.js文件,用法和Loader配置中一樣 loaders:['babel-loader?cacheDirectory'], threadPool: HappyPackThreadPool, }) ] 復制代碼

13、PWA優化策略

簡言之:在你第一次訪問一個網站的時候,如果成功,做一個緩存,當服務器掛了之后,你依然能夠訪問這個網頁 ,這就是PWA。那相信你也已經知道了,這個只需要在生產環境,才需要做PWA的處理,以防不測。

 cnpm i workbox-webpack-plugin -D

const WorkboxPlugin = require('workbox-webpack-plugin') // 引入 PWA 插件 const prodConfig = { plugins: [ // 配置 PWA new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ] } 在入口文件加上 // 判斷該瀏覽器支不支持 serviceWorker if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(registration => { console.log('service-worker registed') }) .catch(error => { console.log('service-worker registed error') }) }) } 復制代碼

配置完后,你可以打包到dist目錄下,在dist目錄下啟動一個靜態服務器,訪問首頁,然后關閉這個服務器,你會驚訝的發現:網站竟然還能夠訪問,哈哈,是不是很神奇?

15、合並提取webpack公共配置

 開發環境與生產環境以及webpack配置文件的分離,具體需要用到webpack-merge,用來 合並 webpack配置
復制代碼

16、最終分離配置文件

由於時間和篇幅的限制,基本到這里就結束了。以上,不管是提到的未提到的,或者還有一些細枝末節,github上的源碼基本都已經全部包括在內了,如果有需要可以去github參照配置文件,自己跟着配一份出來,會更加事半功倍,線上代碼react-webpack4-cook

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM