1. 程式人生 > >從零搭建React開發腳手架,基於[email protected]

從零搭建React開發腳手架,基於[email protected]

前言

[email protected]已經遷移到4,將目前前端最主流,應用最廣的webpack總結下

目前兩個常用的構建工具

  1. facebook官方的create-react-app(官方推薦)
    基於[email protected]的版本

  2. 社群的generator-react-webpack(可配置性高)
    基於[email protected]的版本

(二)webpack基本概念

  • 入口(entry)
  • 輸出(output)
  • loader
  • 外掛(plugins)
  • entry
    webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始。進入入口起點後,webpack 會找出有哪些模組和庫是入口起點(直接和間接)依賴的。
    配置引數
  • output
    output 屬性告訴 webpack 在哪裡輸出它所建立的 bundles,以及如何命名這些檔案。
    配置引數
  • loader
    webpack loader 將所有型別的檔案,轉換為應用程式的依賴圖(和最終的 bundle)可以直接引用的模組。

    配置中 loader 有兩個目標:
    test 屬性,用於標識出應該被對應的 loader 進行轉換的某個或某些檔案。
    use 屬性,表示進行轉換時,應該使用哪個 loader。

    配置引數

  • plugins
    外掛目的在於解決 loader 無法實現的其他事。比如 壓縮js(uglifyjs-webpack-plugin),壓縮css(optimize-css-assets-webpack-plugin),將js中的css分離獨立出來(extract-text-webpack-plugin)等

配置引數

(三)搭建webpack基礎環境

  • 初始化專案目錄
$ mkdir webpack-demo
$ cd webpack-demo
$ npm init -y // -y的意思是預設安裝

新建四個檔案,分別是webpack.base.js(基礎環境),webpack.dev.js(開發環境),webpack.prod.js(生產環境),.gitignore(git忽略檔案)

這時候我們的檔案目錄為

檔案結構

  • 配置webpack基本環境設定
    注意:-S是安裝在生產環境,-D安裝在開發環境。
 // 模組管理和打包工具
$ npm install [email protected] -D
// 監聽程式碼自動重新整理(注意@3版本對應
[email protected]
) $ npm install [email protected] -D // 安裝merge $ npm i webpack-merge -D // 安裝html處理 $ npm i html-webpack-plugin -D // 清除外掛 $ npm i clean-webpack-plugin -D // copy 外掛 $ npm i copy-webpack-plugin -D // 安裝dev open-browser $ npm i open-browser-webpack-plugin -D // 安裝 lodash $ npm install lodash -S

新建public和src資料夾,專案目錄為下圖
專案目錄

配置webpack.base.js

/**
 * @component webpack.base.js
 * @description 基礎環境
 * @time 2018/3/8
 * @author **
 */
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 生成html
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 清除dist
const CopyWebpackPlugin = require('copy-webpack-plugin'); // copy

function resolve(dir) {
    return path.join(__dirname, dir);
}

module.exports = {
    entry: {
        app: './src/index.js',
        vendor: [
            'lodash'
        ]
    },
    resolve: {
        extensions: [' ', '.js', '.json', '.jsx', '.css', '.less','.json'],
        modules: [resolve( "src"), "node_modules"], //絕對路徑;
    },
    module: {
    },
    plugins: [
        new CleanWebpackPlugin(['dist']),
        new CopyWebpackPlugin([{
            from: "./public",
            to: "",
            force: true
        }]),
        new HtmlWebpackPlugin({
            filename: 'index.html',//輸出的html路徑
            template: './public/index.html', //html模板路徑
            chunks: ['app', 'vendor', 'manifest'],
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyCSS: true,
                minifyJS: true,
                minifyURLs: true,
            }
        }),
        new webpack.HashedModuleIdsPlugin(), // 修復vendor hash
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: Infinity
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'manifest', // 指定公共 bundle 的名稱。
            minChunks: Infinity
        })
    ]
};

配置webpack.dev.js

/**
 * @component webpack.dev.js
 * @description 開發環境
 * @time 2018/3/8
 * @author **
 */
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const base = require('./webpack.base.js');

function resolve (dir) {
    return path.join(__dirname, dir)
}
// 埠
const port = 8080;

module.exports = merge(base, {
    output: {
        filename: 'static/js/[name].js', //
        path: resolve('dist'), // 輸出的檔案地址
        publicPath: ''
    },
    devtool: 'inline-source-map',
    module: {
    },
    devServer: {
        contentBase: './dist',
        compress: true,
        port: port,
        historyApiFallback: true,//不跳轉
        inline: true//實時重新整理
    },
    plugins: [
    new webpack.NamedModulesPlugin(),
	new webpack.HotModuleReplacementPlugin(),
        new OpenBrowserPlugin({ url: 'http://localhost:'+port }),
    ],
});

配置webpack.prod.js

/**
 * @component webpack.prod.js
 * @description 生產環境
 * @time 2018/3/8
 * @author **
 */
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const base = require('./webpack.base.js');

function resolve (dir) {
    return path.join(__dirname, dir)
}

module.exports = merge(base, {
    output: {
        filename: 'static/js/[name].[hash:7].js', //
        path: resolve('dist'), // 輸出的檔案地址
        publicPath: './'
    },
    devtool: 'source-map',
    module: {
    },
    plugins: [
	    new webpack.optimize.UglifyJsPlugin({ // 壓縮JS
            compress: {
                warnings: false,
                comparisons: false,
            },
            mangle: {
                safari10: true,
            },
            output: {
                comments: false,
                ascii_only: true,
            },
            sourceMap: true,
        }),
    ]
});

src新增index.js

import _ from 'lodash';

function component() {
    var element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    element.classList.add('hello');

    return element;
}

document.body.appendChild(component());

public新增index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Getting Started</title>
</head>
<body>
</body>
</html>

修改package.json

"scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

分別執行 npm run dev(開發) / npm run build(釋出)

演示目的,我們使用開發演示修改程式碼。
這裡寫圖片描述

(四)增加css處理

// 安裝相關loader
$ npm install style-loader css-loader less-loader postcss-loader postcss-flexbugs-fixes -D
$ npm i autoprefixer -D
$ npm i less -D
// 將css分離出js
$ npm i extract-text-webpack-plugin -D

配置webpack.prod.js

const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const path = require('path');
const merge = require('webpack-merge');
const ExtractTextPlugin = require("extract-text-webpack-plugin"); // 分離css
const base = require('./webpack.base.js');

function resolve (dir) {
    return path.join(__dirname, dir)
}
module.exports = merge(base, {
    output: {
        filename: 'static/js/[name].[hash:7].js', //
        path: resolve('dist'), // 輸出的檔案地址
        publicPath: './'
    },
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract({
                    fallback: {
                        loader: require.resolve('style-loader'),
                        options: {
                            hmr: false,
                        },
                    },
                    use: [
                        {
                            loader: require.resolve('css-loader'),
                            options: {
                                importLoaders: 1,
                                minimize: true,
                                sourceMap: true,
                            },
                        },
                        {
                            loader: require.resolve('postcss-loader'),
                            options: {
                                ident: 'postcss',
                                plugins: () => [
                                    require('postcss-flexbugs-fixes'),
                                    autoprefixer({
                                        browsers: [
                                            '>1%',
                                            'last 4 versions',
                                            'Firefox ESR',
                                            'not ie < 9', // React doesn't support IE8 anyway
                                        ],
                                        flexbox: 'no-2009',
                                    }),
                                ],
                            },
                        },
                    ],
                }),
            },
            {
                test: /\.less$/,
                loader: ExtractTextPlugin.extract({
                    fallback: {
                        loader: require.resolve('style-loader'),
                        options: {
                            hmr: false,
                        },
                    },
                    use: [
                        {
                            loader: require.resolve('css-loader'),
                            options: {
                                importLoaders: 1,
                                minimize: true,
                                sourceMap: true,
                            },
                        },
                        {
                            loader: require.resolve('postcss-loader'),
                            options: {
                                ident: 'postcss',
                                plugins: () => [
                                    require('postcss-flexbugs-fixes'),
                                    autoprefixer({
                                        browsers: [
                                            '>1%',
                                            'last 4 versions',
                                            'Firefox ESR',
                                            'not ie < 9', // React doesn't support IE8 anyway
                                        ],
                                        flexbox: 'no-2009',
                                    }),
                                ],
                            },
                        },
                        {
                           loader: require.resolve('less-loader'),
                           options: {
                               importLoaders: 2,
                               minimize: true,
                               sourceMap: true,
                           },
                       },
                    ],
                }),
                // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
            },
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin({ // 壓縮JS
            compress: {
                warnings: false,
                comparisons: false,
            },
            mangle: {
                safari10: true,
            },
            output: {
                comments: false,
                ascii_only: true,
            },
            sourceMap: true,
        }),
        new ExtractTextPlugin({
            filename: "static/css/[name].[hash:7].css",
            allChunks: true
        }),
    ]
});

配置webpack.dev.js

const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const path = require('path');
const merge = require('webpack-merge');
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const base = require('./webpack.base.js');

function resolve (dir) {
    return path.join(__dirname, dir)
}
// 埠
const port = 8080;
module.exports = merge(base, {
    output: {
        filename: 'static/js/[name].js', //
        path: resolve('dist'), // 輸出的檔案地址
        publicPath: ''
    },
    devtool: 'inline-source-map',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    require.resolve('style-loader'),
                    {
                        loader: require.resolve('css-loader'),
                        options: {
                            importLoaders: 1,
                        },
                    },
                    {
                        loader: require.resolve('postcss-loader'),
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require('postcss-flexbugs-fixes'),
                                autoprefixer({
                                    browsers: [
                                        '>1%',
                                        'last 4 versions',
                                        'Firefox ESR',
                                        'not ie < 9', // React doesn't support IE8 anyway
                                    ],
                                    flexbox: 'no-2009',
                                }),
                            ],
                        },
                    },
                ],
            },
            {
                test: /\.less$/,
                use: [
                    require.resolve('style-loader'),
                    {
                        loader: require.resolve('css-loader'),
                        options: {
                            importLoaders: 1,
                        },
                    },
                    {
                        loader: require.resolve('postcss-loader'),
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require('postcss-flexbugs-fixes'),
                                autoprefixer({
                                    browsers: [
                                        '>1%',
                                        'last 4 versions',
                                        'Firefox ESR',
                                        'not ie < 9', // React doesn't support IE8 anyway
                                    ],
                                    flexbox: 'no-2009',
                                }),
                            ],
                        },
                    },
                    {
                        loader: require.resolve('less-loader'),
                        options: {
                            importLoaders: 2,
                        },
                    },
                ],
            },
        ]
    },
    devServer: {
        contentBase: './dist',
        compress: true,
        port: port,
        historyApiFallback: true,//不跳轉
        inline: true//實時重新整理
    },
    plugins: [
        new OpenBrowserPlugin({ url: 'http://localhost:'+port }),
    ],
});

src修改
新增index.css, test.less

/**index.css**/
body {
    background-color: aqua;
}
/**test.less**/
.hello {
    color: red;
    transition: all .5s;
}
/**index.js新增**/
import './index.css'
import './test.less'

npm run build檢視打包dist資料夾

成功

(五)增加image, font, babel處理, 壓縮js,react

// image和font處理
$ npm i file-loader url-loader -D
//減少打包的時候重複程式碼
$ npm i babel-runtime  -S
$ npm i babel-plugin-transform-runtime -D

//安裝babel相關
$ npm i babel-loader -D //安裝babel-loader
$ npm i babel-core -D //安裝babel核心
$ npm i babel-preset-es2015 -D //支援ES2015
$ npm i babel-preset-react -D  //支援jsx
$ npm i babel-preset-stage-0 -D //支援ES7

//安裝react
$ npm i react -S
$ npm i react-dom -S

專案結構
這裡寫圖片描述

webpack.base.js新增module,修改entry

entry: {
	  app: './src/index.js',
	  vendor: [
	    'react',
	    'react-dom'
	  ]
},
module: {
        rules: [
            {
                test: /\.(js|jsx)?$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                include: [resolve('src'), resolve('test')]
            },
            {
                test: /\.(png|svg|jpg|gif|jpeg)$/, use:
                [{
                    loader: 'url-loader',
                    options: {
                        fallback: 'file-loader',
                        limit: 8192,
                        outputPath: 'static/images/',
                        name: '[name]_[hash:7].[ext]',
                    }
                }]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/, use:
                [{
                    loader: 'file-loader',
                    options: {
                        outputPath: 'static/fonts/',
                        name: '[name]_[hash:7].[ext]',
                    }
                }]
            }
        ]
    },

在public/index.html

<body>
<!--新增-->
<div id="root"></div>
</body>
</html>

新建.babel

{
  "presets": [ "es2015", "stage-0", "react"],
  "env": {
    "build": {
      "optional": ["optimisation", "minification"]
    }
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import _ from 'lodash';
import './index.less';

if (process.env.NODE_ENV !== 'production') {
  console.log('Looks like we are in development mode!!!!');
}

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

App.jsx

import React, { Component } from 'react';
import Icon from './logo.svg';
import loginBg from './login-bg.jpg';
import './App.less';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={Icon} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          <img src={loginBg}/>
        </p>
      </div>
    );
  }
}

export default App;

相關程式碼已推送到github