使用webpack4從0開始打包一個antd項目

初始化項目

$ npm install -g cnpm --registry=https://registry.npm.taobao.org // 安裝cnpm

$ mkdir project // 根目錄下創(chuàng)建一個名為project的文件夾

$ cd project && cnpm init // 進(jìn)入project文件夾 并初始化該項目 終端會提示一些配置口猜,一路鍵入enter最后yes即可。 初始化完成會生成一個pakage.json文件

安裝webpack (4.x)

$ cnpm i --D webpack webpack-dev-server webpack-cli // webpack4.x 必須安裝webpack-cli。

根目錄下新建文件src/index.js

image

$ npm run build // 此時我們可以執(zhí)行npm run build 代替npx webpack涝滴。 構(gòu)建成功后根目錄下自動生成 dist/main.js雷逆。

為什么我們沒有對webpack進(jìn)行任何配置骇两,卻可以成功打包寿酌?那是因為在沒有配置入口文件的情況下掉瞳, webpack 4.x會自動查找src/index.js作為入口文件進(jìn)行打包饮醇。

webpack配置

這里我們需要了解webpack4個核心概念——入口(entry)入桂、輸出(output)、loader驳阎、插件(plugins)抗愁。

1.對js文件進(jìn)行打包

根目錄下創(chuàng)建webpack.config.js

const path = require("path");

module.exports = {
    // 指定構(gòu)建環(huán)境  
    mode:"development",
    // 入口
    entry: {
      main: path.resolve(__dirname, './src/index.js')  // 配置入口文件為src/index.js
    },
    // 出口
    output: {
        path : path.resolve(__dirname, "./dist"), // 出口文件放到dist文件夾下
        filename: "[name].js",  // 這里的[name] 取的是 entry里的main,如果entry里配置的為app: path.resolve(__dirname, './src/index.js') 則打包出的文件為dist/app.js
        publicPath: "/" // 打包后的資源的訪問路徑前綴
    },
    // 模塊
    module:{
    },
    // 插件
    plugins:[
    ],
    // 開發(fā)環(huán)境本地啟動的服務(wù)配置
    devServer: {
    }
}

$ cnpm run build
我們可以看到根目錄下打包出了dist/main.js呵晚。
這時候如果我們將webpack.config.js中入口配置改為

entry: {
  app: path.resolve(__dirname, './src/index.js')
}

$ cnpm run build


截屏2020-06-13 下午9.28.13.png

重新構(gòu)建我們發(fā)下dist目錄下多出了一個app.js文件蜘腌,上一次打包的main.js還在,如果我們希望每次打包之前先清除掉上一次的打包文件饵隙,則需要用到clean-webpack-plugin插件撮珠。

$ cnpm i -D clean-webpack-plugin // vesion: 6.13.4 (6.x版本與低版本引入方式不一樣)
安裝后我們在webpack.config.js里進(jìn)行配置 。
首先引入clean-webpack-plugin


[圖片上傳中...(截屏2020-06-13 下午9.33.58.png-d29026-1592055241921-0)]

再在plugins里實例化


截屏2020-06-13 下午9.34.40.png

然后我們將 entry: { app: {...}} 中的app改回main

$ cnpm run build
重新構(gòu)建金矛,我們可以看到上一次打包出的dist/app.js已經(jīng)刪掉了芯急。

2.增加html文件

首先我們在dist目錄下手動增加一個index.html文件勺届,引入main.js

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>react-music</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
</head>

<body>
  <div id="root"></div>
  <script src="main.js"></script>
</body>
</html>

在chrome中打開該html文件,我們可以看到控制臺中成功打印出hello world


截屏2020-06-13 下午9.45.25.png

那么問題來了娶耍,難道我們每次打包需要手動在dist目錄下添加一個html文件嗎免姿? 顯然不可能,這時候我們需要用到html-webpack-plugin插件自動添加html文件榕酒。

我們先將dist/index.html文件移動到src下胚膊,去掉<script>標(biāo)簽。
$ cnpm i -D html-webpack-plugin
安裝好后一樣先引入插件 具體插件配置參考官網(wǎng)

const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
    template: 'src/index.html', // 自動生成的html文件以src/index.html為模板生成
    inject: true, // true:默認(rèn)值想鹰,script標(biāo)簽位于html文件的 body 底部
    hash: true, // 在打包的資源插入html會加上hash
    minify: {
      removeComments: true,               //去注釋
      collapseWhitespace: true,           //壓縮空格
      removeAttributeQuotes: true         //去除屬性 標(biāo)簽的 引號  例如 <p id="test" /> 輸出 <p id=test/>
    }
  }),
]

$ cnpm run build
此時我們看到dist目錄下自動生成了一個index.html文件紊婉,里面包含了<div id="root"></div>,
這是因為我們在模板文件里寫過。之前我們已經(jīng)安裝過了webpack-dev-server辑舷,并在package.json腳本里配置了"dev": "webpack-dev-server" 此時我們可以運行

$ cnpm run dev
終端提示自動運行在localhost:8080, chrome上打開8080端口喻犁,查看控制臺,我們可以看到打印出了hello world何缓。

3.豐富webpack-dev-server配置
  // 開發(fā)環(huán)境本地啟動的服務(wù)配置
  devServer: {
    historyApiFallback: true, // 當(dāng)找不到路徑的時候株汉,默認(rèn)加載index.html文件
    hot: true,
    contentBase: false, // 告訴服務(wù)器從哪里提供內(nèi)容。只有在你想要提供靜態(tài)文件時才需要
    compress: true, // 一切服務(wù)都啟用gzip 壓縮:
    port: "8989", // 指定端口號
    publicPath: "/", // 訪問資源加前綴
    proxy: {
        // 接口請求代理
    },
  }

$cnpm run dev


截屏2020-06-14 上午9.48.49.png

這時我們可以看到該項目已經(jīng)運行在我們制定的端口8989上了歌殃。

打包css/less 乔妈、圖片、字體等氓皱。

$ cnpm i -D style-loader css-loader less-loader file-loader url-loader
我們在src目錄下新建一個images文件夾路召,然后拖入一張圖片,這里圖片名用了smokinggirl.jpeg波材。 再在src目錄下新建一個index.less文件股淡。
然后修改src/index.js src/index.less

import advatar from './images/smokinggirl.jpeg'; // 引入圖片地址
import './index.less'; // 引入index.less

const root = document.getElementById('root');
const img = new Image();
img.src = advatar;
img.classList.add('advatar'); // 為圖片增加類
root.appendChild(img);

src/index.less

@img-width: 200px;

.advatar {
  width: @img-width;
}

webpack.config.js中module部分增加配置

    module:{
      rules: [{
        test: /\.(jpg|jpeg|png|gif)$/,   // 正則匹配文件后綴
        use: {
          loader: 'file-loader',
          options: {
            name: 'images/[name].[ext]'
          }
        }
      }, {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'], 
      }, {
        test: /\.less$/,
        loader: 'style-loader!css-loader!less-loader'  // 等同于use: ['style-loader', 'css-loader', 'less-loader']
      }]
    },

$ cnpm run dev
http://localhost:8989 可以看到有一個寬200px的圖片顯示出來。
這樣打包出來的css 樣式會作用到全局廷区,如果頁面多可能會相互影響唯灵,這時候我們可以啟用css模塊,配置稍作修改

    // 模塊
    module:{
      rules: [{
        test: /\.(jpg|jpeg|png|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: 'images/[name].[ext]'
          }
        }
      }, {
        test: /\.css$/,
        exclude:/node_modules/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules: true
          }
        }],
      }, {
        test: /\.less$/,
        exclude:/node_modules/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules: true,
          }
        }, {
          loader: 'less-loader',
        }]
      }]
    },

src/index.js

import advatar from './images/smokinggirl.jpeg'; // 引入圖片地址
import styles from './index.less';  // 引入方式改變

const root = document.getElementById('root');
const img = new Image();
img.src = advatar;
img.classList.add(styles.advatar); // 從styles對象里獲取類名
root.appendChild(img);

$cnpm run dev
這時候我們可以看到圖片成功顯示出來隙轻,但仔細(xì)觀察圖片類名埠帕,是一串自動生成的字符,與我們自己定義的類名沒有半點關(guān)系玖绿,那么如何顯示出我們定義的類名呢敛瓷?


截屏2020-06-14 上午11.18.35.png
   {
        test: /\.less$/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',  // 這里可以自定義以什么樣的形式打包出類名。
            }
          }
        }, {
          loader: 'less-loader',
        }]
   }

$ cnpm run dev 這時候可以看到打包出的類名符合我們定義的形式斑匪。


截屏2020-06-14 上午11.24.37.png

但仔細(xì)觀察呐籽,我們發(fā)現(xiàn)body還有默認(rèn)樣式,我們可以在index.html引入一個公共樣式文件,去除默認(rèn)樣式狡蝶。
src目錄下 新建common/reset-style.css, 寫個簡單的去默認(rèn)樣式的文件

body {
  margin: 0;
  padding: 0;
}
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

然后在html模板文件 src/index.html中引入庶橱,

<head>
  <meta charset="utf-8">
  <title>project</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <link rel="stylesheet" href="../common/reset-style.css">
</head>

這時候我們運行,發(fā)現(xiàn)報錯找不到該文件贪惹。這是因為我們在index.html里引入該文件苏章,但webpack沒有打包編譯。這時候我們需要用到插件copy-webpack-plugin
$ cnpm i -D copy-webpack-plugin

const CopyWebpackPlugin = require('copy-webpack-plugin');
    plugins:[
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        template: 'src/index.html',
        inject: true, // true:默認(rèn)值馍乙,script標(biāo)簽位于html文件的 body 底部
        hash: true, // 在打包的資源插入html會加上hash
        minify: {
          removeComments: true,               //去注釋
          collapseWhitespace: true,           //壓縮空格
          removeAttributeQuotes: true         //去除屬性 標(biāo)簽的 引號  例如 <p id="test" /> 輸出 <p id=test/>
        }
      }),
      new CopyWebpackPlugin({
        patterns: [
          {
            from: path.resolve(__dirname, './common'),  // 從哪個目錄copy
            to: "common", // copy到那個目錄
            globOptions: {
              ignore: ['.*'],
            }
          }
        ],
      }),
    ],

重新運行,我們可以看到原先的默認(rèn)樣式body的邊距已經(jīng)沒有了垫释。

引入React

$ npm i -S react react-dom
安裝完react丝格、react-dom 還不夠,我們需要babel做翻譯棵譬,才能讓瀏覽器讀懂显蝌。

$ cnpm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime @babel/runtime-corejs2

  • @babel/preset-react 翻譯react語法
  • @babel/preset-env es6、es7轉(zhuǎn)換成es5
  • @babel/plugin-transform-runtime 對@babel/preset-env無法轉(zhuǎn)換的es6订咸、es7的新特性進(jìn)行轉(zhuǎn)換曼尊。
  • @babel/runtime只能處理語法關(guān)鍵字,而@babel/runtime-corejs2還能處理新的全局變量(例如脏嚷,Promise)骆撇、新的原生方法(例如,String.padStart )父叙;因此我們使用@babel/runtime-corejs2 就無須再使用 @babel/runtime了神郊。

安裝好后根目錄新建.babelrc文件。

{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
      "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以減小打包的體積
    }]
  ]
}

webpack.config.js 的module里增加babel-loader的配置

 {
      test: /\.jsx?$/,//一個匹配loaders所處理的文件的拓展名的正則表達(dá)式趾唱,這里用來匹配js和jsx文件(必須)
      exclude: /node_modules/,//屏蔽不需要處理的文件(文件夾)(可選)
      loader: 'babel-loader',//loader的名稱(必須) loader接收字符串涌乳, use接收對象或數(shù)組
  }

配置好我們來寫react文件
新建src/page/content/index.js, 將src下的index.less拖入content目錄下。

import React, { Component } from 'react';
import advatar from '../../images/smokinggirl.jpeg'; // 引入圖片地址
import styles from './index.less';

class Content extends Component {
  render() {
    return (
      <div>
        <h2>這是一張圖片</h2>
        <img src={advatar} className={styles.advatar} />
      </div>
    )
  }
}
export default Content

接下來改造src/index.js 入口文件

import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import Content from './page/content';

class App extends PureComponent {
  render() {
    return (
      <div>
        <Content />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

然后運行 我們可以看到頁面正常顯示甜癞。到這里我們已經(jīng)可以正確打包并運行react項目了夕晓。

引入antd

$ cnpm i -S antd
根據(jù)antd官網(wǎng)描述,我們還需要在入口文件引入一個樣式文件。
src/index.js

import 'antd/dist/antd.css';

到這里我們可以發(fā)現(xiàn)一個問題,我們對css文件打包的時候啟用了模塊峭状,很明顯這樣直接引用是不行的茬斧。 antd的樣式是從node_modules里引用的,那么我們的思路就是打包css文件時去除掉node_modules里面的文件再啟用模塊吱晒,node_modules里面的文件不啟用模塊。
接下來我們對webpack.config.json進(jìn)行改造。

    // 模塊
    module:{
      rules: [{
        test: /\.(jpg|jpeg|png|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: 'images/[name].[ext]'
          }
        }
      }, {
        test: /\.css$/,
        exclude:/node_modules/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }],
      }, {
        test: /\.css$/,
        include: /node_modules/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules:  false,
          }
        }]
      }, {
        test: /\.less$/,
        exclude:/node_modules/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }, {
          loader: 'less-loader',
        }]
      }, {
        test: /\.jsx?$/,//一個匹配loaders所處理的文件的拓展名的正則表達(dá)式逗宜,這里用來匹配js和jsx文件(必須)
        exclude: /node_modules/,//屏蔽不需要處理的文件(文件夾)(可選)
        loader: 'babel-loader',//loader的名稱(必須) loader接收字符串, use接收對象或數(shù)組
      }]
    },

只有這樣還不夠,我們需要用到babel-plugin-import插件實現(xiàn)按需加載纺讲,antd樣式應(yīng)用到全局需要用到此插件擂仍。
$cnpm i -D babel-plugin-import
.babelrc

  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
      "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以減小打包的體積
    }],
    ["import", { // babel-plugin-import 按需加載插件 項目組件css使用modules方式打包,antd樣式全局引用 需要用到此插件熬甚。
      "libraryName":"antd",
      "libraryDirectory":"es",
      "style": "css"
    }]
  ]

ok現(xiàn)在我們來改造一下src/page/content/index.js文件逢渔,引入一個antd組件。

import React, { Component } from 'react';
import { Radio } from 'antd';
import advatar from '../../images/smokinggirl.jpeg'; // 引入圖片地址
import styles from './index.less';

class Content extends Component {
  constructor(props) {
    super(props);
    this.state = {
      type: 'img',
    }
  }

  handleChange = e => {
    const val = e.target.value;
    this.setState({
      type: val,
    });
  }

  render() {
    const { type } = this.state;
    return (
      <div>
        <Radio.Group value={type} onChange={this.handleChange}>
          <Radio value="img">圖片</Radio>
          <Radio value="text">文本</Radio>
        </Radio.Group>
        <h2>{type === 'img' ? '這是一張圖片' : '這是一段文案'}</h2>
        {
          type === 'img'
          ? 
          <img src={advatar} className={styles.advatar} />
          :
          <p>這是一大段文案</p>
        }
      </div>
    )
  }
}
export default Content

現(xiàn)在我們來運行乡括,發(fā)現(xiàn)報了個錯


截屏2020-06-14 下午3.35.50.png

提示我們需要安裝一個@babel/plugin-proposal-class-properties插件肃廓。
$ cnpm i -D @babel/plugin-proposal-class-properties
然后我們需要在babel的配置文件中加入它

  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "corejs": 2, // polyfill 需要使用@babel/runtime-corejs2
      "useBuildIns":"usage", //按需引入,即使用什么新特性打包什么新特性, 可以減小打包的體積
    }],
    ["import", { // babel-plugin-import 按需加載插件 項目組件css使用modules方式打包,antd樣式全局引用 需要用到此插件诲泌。
      "libraryName":"antd",
      "libraryDirectory":"es",
      "style": "css"
    }],
    ["@babel/plugin-proposal-class-properties"]
  ]

現(xiàn)在我們重新運行 $ cnpm run dev 一個簡單的包含antd組件的頁面就完成了盲赊。


截屏2020-06-14 下午3.38.46.png

截屏2020-06-14 下午3.39.14.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敷扫,一起剝皮案震驚了整個濱河市哀蘑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葵第,老刑警劉巖绘迁,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卒密,居然都是意外死亡缀台,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門哮奇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來将硝,“玉大人,你說我怎么就攤上這事屏镊∫捞郏” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵而芥,是天一觀的道長律罢。 經(jīng)常有香客問我,道長棍丐,這世上最難降的妖魔是什么误辑? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮歌逢,結(jié)果婚禮上巾钉,老公的妹妹穿的比我還像新娘。我一直安慰自己秘案,他們只是感情好砰苍,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布潦匈。 她就那樣靜靜地躺著,像睡著了一般赚导。 火紅的嫁衣襯著肌膚如雪茬缩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天吼旧,我揣著相機(jī)與錄音凰锡,去河邊找鬼。 笑死圈暗,一個胖子當(dāng)著我的面吹牛掂为,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播员串,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼勇哗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昵济?” 一聲冷哼從身側(cè)響起智绸,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤野揪,失蹤者是張志新(化名)和其女友劉穎访忿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斯稳,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡海铆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挣惰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卧斟。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖憎茂,靈堂內(nèi)的尸體忽然破棺而出珍语,到底是詐尸還是另有隱情,我是刑警寧澤竖幔,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布板乙,位于F島的核電站,受9級特大地震影響拳氢,放射性物質(zhì)發(fā)生泄漏募逞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一馋评、第九天 我趴在偏房一處隱蔽的房頂上張望放接。 院中可真熱鬧,春花似錦留特、人聲如沸纠脾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乳乌。三九已至捧韵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汉操,已是汗流浹背再来。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留磷瘤,地道東北人芒篷。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像采缚,于是被迫代替她去往敵國和親针炉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355