webpack+express+react系列一(webpack配置)

最近是己,發(fā)現(xiàn)自己離前端新技術(shù)接觸越來(lái)越少了,由于用不上任柜,連以前接觸過(guò)的webpack卒废、react這些都開始淡忘沛厨。本著一顆學(xué)習(xí)的心,打算在工作之余摔认,抽取一部分時(shí)間來(lái)鉆研逆皮,搭建一個(gè)簡(jiǎn)單的管理平臺(tái)。
基于技術(shù)上的發(fā)展级野、穩(wěn)定性及目前提倡的自動(dòng)化和模塊化開發(fā)页屠,本人暫時(shí)確定采用webpack/express/react/react-router/redux。

前端自動(dòng)化和模塊化 -- 前端工具webpack的使用
  • 在開始之前蓖柔,先看下項(xiàng)目的結(jié)構(gòu)(由于項(xiàng)目采用的是express腳手架來(lái)生成的辰企,后續(xù)添加的內(nèi)容基本以此為基準(zhǔn))
項(xiàng)目目錄結(jié)構(gòu)

Paste_Image.png

public文件夾為前端開發(fā)目錄,dist為打包后目錄况鸣。

webpack配置牢贸。
  • views中的html采用的是插件HtmlWebpackPlugin生成的
// 遍歷所有模板中的.html文件,使用HtmlWebpackPlugin引入靜態(tài)資源(或者用ejs模板生成多個(gè))
var htmlfiles = fs.readdirSync(path.resolve(__dirname, "./src/public/templates"));
htmlfiles.forEach(function(item) {
    var currentpath = path.resolve(__dirname+"/src/public/templates", item);
    var extname = path.extname(currentpath);
    if(fs.statSync(currentpath).isFile() && (extname == ".html" || extname == ".ejs")) {
        var json = {
            template: currentpath, // 設(shè)置引入的模板
            filename: path.join(__dirname, './src/views/'+item), // 設(shè)置輸出路徑
            inject: 'body', // 設(shè)置js插入位置
            hash: true, // 為所有的資源加上hash值
            chunks: ["style", "vendor1", "vendor2", "common", "chunk", "bundle"], // 指定引入的資源
            showErrors: true // 是否展示錯(cuò)誤
        }
        // 生產(chǎn)環(huán)境壓縮html
        if(isPro) {
            json["minify"] = {
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
            }
        }
        config.plugins.push(
            new HtmlWebpackPlugin(json)
        );
    }
});
  • 設(shè)置環(huán)境變量process.env.NODE_ENV
// 在package.json中設(shè)置
"start": "set NODE_ENV=development && supervisor -i ./src/public ./src/bin/www"
  • 采用ExtractTextPlugin和postcss-loader處理樣式镐捧。在js中采用require引入css樣式表潜索,webpack默認(rèn)將其合并到編譯后的文件中,故需要將其抽取出來(lái)懂酱,做轉(zhuǎn)換和兼容處理竹习。
## 在loader中添加
{
                // 識(shí)別js中require引入的樣式表,并將其轉(zhuǎn)換和兼容處理
                test: /\.(css|scss|sass)$/,
                exclude: /node_modules/,
                use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        {
                            loader: 'css-loader',
                            options: { autoprefixer: true, sourceMap: true, importLoaders: 1 }
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                sourceMap: true,
                                plugins: () => [autoprefixer({ browsers: ['last 2 versions', 'safari >= 5', 'ie 8', 'ie 9','iOS >= 7', 'Android >= 4.1'] })],
                            }
                        },
                        'sass-loader'
                    ]
                }))
            }

## 在plugins中添加
// 抽離樣式
        new ExtractTextPlugin({
            filename:  (getPath) => {
                return getPath('css/style.css');
            },
            allChunks: true
        }),
  • 用加載器url-loader對(duì)圖片進(jìn)行處理
{
      // 圖片加載器列牺,可以將較小的圖片轉(zhuǎn)成base64整陌,減少http請(qǐng)求
      // 如下配置,將小于8kb的圖片轉(zhuǎn)成base64碼瞎领,大于8192byte的圖片將通過(guò)file-loader把源文件遷移到指定的路徑泌辫,并返回新的路徑
       // [hash]/[name] hash值,防止重名九默,如兩張圖片一樣震放,只會(huì)生成同一hash
       test: /\.(png|jpg|gif|svg)$/,
       loader: [
                  'url-loader?limit=8192&name=images/[hash].[ext]',
                  'image-webpack-loader'
                ],
              
  • 對(duì)js進(jìn)行轉(zhuǎn)換處理
##  loader中添加
// 在新版中,loaders中的加載器驼修,不能以"es3ify!babel"這樣連起來(lái)殿遂,要么數(shù)組,要么"es3ify-loader!babel-loader"
{
      // es3ify-loader 兼容ie8乙各,將es5轉(zhuǎn)譯成es3
      test: /\.js$/,
      exclude: /node_modules/,
      loaders: ["es3ify-loader", "babel-loader"]
}
  • 設(shè)置自動(dòng)補(bǔ)全文件后綴
resolve: {
        extensions: ['*', '.js', '.jsx', ".css", ".scss"] // 值得注意的是勉躺,在新版中,數(shù)組中不能存在''觅丰,而是用'*'替代
} 
  • 使用插件CommonsChunkPlugin抽取公共依賴模塊
new webpack.optimize.CommonsChunkPlugin({
     name: 'chunk',
    chunks: ['vendor2', 'bundle'] // 這里是output生成的輸出文件
}),
  • 使用插件CopyWebpackPlugin來(lái)拷貝資源,主要用于第三方插件/庫(kù)的拷貝妨退,配合webpack的externals配置妇萄,可直接在html中引用cdn/本地第三方插件/庫(kù)蜕企,而不會(huì)被webpack打包
// 拷貝資源
new CopyWebpackPlugin(
[{
     from: './src/public/plugins/',
     to: 'plugins'
 }]
)
  • 使用插件UglifyJsPlugin壓縮代碼
new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            mangle: {
                except: ['jQuery', '$', 'exports', 'require', "module"]
            }
  })
  • noParse、alias和ProvidePlugin搭配
// 設(shè)置引入的插件別名冠句,重定向到指定的文件轻掩,阻止webpack對(duì)require引用的文件進(jìn)行遍歷,查詢其依賴等
alias: {
            react : path.join(__dirname, "node_modules/react/dist/react.min.js"),
            "react-dom" : path.join(__dirname, "node_modules/react-dom/dist/react-dom.min.js")
        }

// 設(shè)置自動(dòng)引入相應(yīng)模塊功能
// 當(dāng)腳本有引入以下變量時(shí)懦底,會(huì)自動(dòng)加載引入相對(duì)應(yīng)的模塊
 new webpack.ProvidePlugin({
            React: 'react',
            ReactDOM: 'react-dom'
  })

// 默認(rèn)的require()方法會(huì)在webpack打包的時(shí)候去解壓唇牧,遍歷ReactJS及其依賴,使用noParse可阻止其默認(rèn)行為
noParse: [
            path.join(__dirname, "node_modules/react/dist/react.min.js"),
            path.join(__dirname, "node_modules/react-dom/dist/react-dom.min.js")
        ]
熱更新

作為一個(gè)會(huì)偷懶的前端聚唐,使用到了webpack丐重,怎么能漏掉其熱更新功能呢。webpack的熱更新功能目前主要有兩種配置方式杆查,一種為webpack-dev-server扮惦,另一種為webpack-dev-middleware和webpack-hot-middleware兩個(gè)中間件配合。其實(shí)webpack-dev-server也是采用webpack-dev-middleware這個(gè)中間件來(lái)實(shí)現(xiàn)的亲桦。由于本人后端采用的是node崖蜜,直接忽略掉了webpack-dev-server。

  • 先裝個(gè)逼
cnpm intstall webpack-dev-middleware --save-dev
cnpm intstall webpack-hot-middleware --save-dev
  • 配置webpack.config.js
var publicPath = "http://127.0.0.1:3000/dist/"; // output中設(shè)置publicPath且publicPath必須為絕對(duì)路徑
var webpackHotMiddleware = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=10000&reload=true';

entry: {
        vendor1 : ["es5-shim", "es5-sham", "console-polyfill", "babel-polyfill", webpackHotMiddleware],
        vendor2 : ["jquery", "react", "react-dom", webpackHotMiddleware],
        common: ["commonExt", webpackHotMiddleware],
        bundle : ["register", webpackHotMiddleware]
    },
output: {
      path: path.resolve(__dirname, './src/dist'),
      publicPath:publicPath,
      filename: 'js/[name].js',
    }
  • 配置app.js(express的配置文件)
// 最好將其放在路由配置之前
if(process.env.NODE_ENV == "production") {
    app.use(express.static(path.join(__dirname, 'dist'),{maxAge:1000*60*60*30}));
  app.use(express.static(path.join(__dirname, '../src'),{maxAge:1000*60*60*30}));
  app.use(express.static(path.join(__dirname, 'tmp'),{maxAge:1000*60*60*30}));
}else{
  var webpack = require("webpack");
  let devMiddleWare = require('webpack-dev-middleware');
  let hotMiddleWare = require('webpack-hot-middleware');
  let webpackconfig = require('../webpack.config.js');
  
  var compiler = webpack(webpackconfig);
  app.use(devMiddleWare(compiler,{
      publicPath: webpackconfig.output.publicPath,
      noInfo: true,
      lazy: false,
      stats: {
        colors: true,
        chunks: false
      }
  }));
  • 在入口文件上添加
// 如我的入口文件為register.js
if (module.hot) {
    module.hot.accept();
}
  • 以上配置只能實(shí)現(xiàn)js的熱更新客峭,css要實(shí)現(xiàn)實(shí)時(shí)刷新效果豫领,就必須引入css-hot-loader
結(jié)語(yǔ)

前奏基本已經(jīng)搞定,剩下的就是一些優(yōu)化了舔琅,不得不感慨前端工具發(fā)展之快等恐,從grunt到gulp再到webpack,前端的打包工具不斷的進(jìn)化搏明,改朝換代的速度讓人不得不感慨鼠锈。每種工具都學(xué)點(diǎn),也懶得深入研究星著,一路挖坑一路填坑购笆,回頭想想,擦虚循,誰(shuí)的鍋M贰!横缔!
附上本人的完整webpack配置

var path = require("path");
var webpack = require("webpack");
var fs = require("fs");
var autoprefixer = require("autoprefixer");
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); // 提取公共部分插件
var HtmlWebpackPlugin = require('html-webpack-plugin'); // 自動(dòng)生成index.html頁(yè)面插件
var CopyWebpackPlugin = require('copy-webpack-plugin'); // 拷貝資源插件
var ExtractTextPlugin = require("extract-text-webpack-plugin"); // 抽離css樣式铺遂,通過(guò)require引入的css,會(huì)被webpack打包到j(luò)s中
var ENV = process.env.npm_lifecycle_event; // 直接通過(guò)獲取啟動(dòng)命令來(lái)判斷開發(fā)/生產(chǎn)環(huán)境
var isPro = process.env.NODE_ENV == "production";
var publicPath = "http://127.0.0.1:3000/dist/";
var webpackHotMiddleware = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=10000&reload=true';

var config = {
    entry: {
        vendor1 : ["es5-shim", "es5-sham", "console-polyfill", "babel-polyfill", webpackHotMiddleware],
        vendor2 : ["jquery", "react", "react-dom", webpackHotMiddleware],
        common: ["commonExt", webpackHotMiddleware],
        bundle : ["register", webpackHotMiddleware]
    },
    output: {
      path: path.resolve(__dirname, './src/dist'),
      publicPath:publicPath,
      filename: 'js/[name].js',
    },
    externals: {
        // jQuery: 'window.$',
        // 'react': 'React',
        // 'react-dom': 'ReactDOM'
    },
    // devtool: 'eval-source-map',
    module: {
        // 默認(rèn)的require()方法會(huì)在webpack打包的時(shí)候去解壓茎刚,遍歷ReactJS及其依賴,
        noParse: [
            path.join(__dirname, "node_modules/react/dist/react.min.js"),
            path.join(__dirname, "node_modules/react-dom/dist/react-dom.min.js"),
            path.join(__dirname, "node_modules/jquery/dist/jquery.min.js"),
            path.join(__dirname, "node_modules/babel-polyfill/dist/polyfill.min.js"),
            path.join(__dirname, "node_modules/console-polyfill/index.js"),
            path.join(__dirname, "node_modules/es5-shim/es5-shim.min.js"),
            path.join(__dirname, "node_modules/es5-shim/es5-sham.min.js")
        ],
        loaders: [
            {
                // es3ify-loader 兼容ie8襟锐,將es5轉(zhuǎn)譯成es3
                test: /\.js$/,
                exclude: /node_modules/,
                loaders: ["es3ify-loader", "babel-loader"]
            },
            {
                // 識(shí)別js中require引入的樣式表,并將其轉(zhuǎn)換和兼容處理
                test: /\.(css|scss|sass)$/,
                exclude: /node_modules/,
                use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        {
                            loader: 'css-loader',
                            options: { autoprefixer: true, sourceMap: true, importLoaders: 1 }
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                sourceMap: true,
                                plugins: () => [autoprefixer({ browsers: ['last 2 versions', 'safari >= 5', 'ie 8', 'ie 9','iOS >= 7', 'Android >= 4.1'] })],
                            }
                        },
                        'sass-loader'
                    ]
                }))
            },
            {
                // 圖片加載器膛锭,可以將較小的圖片轉(zhuǎn)成base64粮坞,減少http請(qǐng)求
                // 如下配置蚊荣,將小于8kb的圖片轉(zhuǎn)成base64碼,大于8192byte的圖片將通過(guò)file-loader把源文件遷移到指定的路徑莫杈,并返回新的路徑
                // [hash]/[name] hash值互例,防止重名,如兩張圖片一樣筝闹,只會(huì)生成同一hash
                test: /\.(png|jpg|gif|svg)$/,
                loader: [
                    'url-loader?limit=8192&name=images/[hash].[ext]',
                    'image-webpack-loader'
                ],
              },
              {
                test: /\.(woff|woff2|eot|ttf)$/,
                loader: 'file-loader?name=fonts/[name].[ext]',
              },
        ]
    },
    resolve: {
        extensions: ['*', '.js', '.jsx', ".css", ".scss"], // 用于自行補(bǔ)全文件后綴
        alias: {
            // 設(shè)置引入的插件別名或配置路徑
            src : path.resolve(__dirname, "./src"),
            public : path.resolve(__dirname, "./src/public"),
            components: path.resolve(__dirname, "./src/public/components"),

            react : path.join(__dirname, "node_modules/react/dist/react.min.js"),
            "react-dom" : path.join(__dirname, "node_modules/react-dom/dist/react-dom.min.js"),
            "babel-polyfill" : path.join(__dirname, "node_modules/babel-polyfill/dist/polyfill.min.js"),
            "console-polyfill" : path.join(__dirname, "node_modules/console-polyfill/index.js"),
            "es5-shim" : path.join(__dirname, "node_modules/es5-shim/es5-shim.min.js"),
            "es5-sham" : path.join(__dirname, "node_modules/es5-shim/es5-sham.min.js"),
            jquery : path.join(__dirname, "node_modules/jquery/dist/jquery.min.js"),
            commonExt: path.resolve(__dirname, "./src/public/js/commonExt"),

            register : path.resolve(__dirname, './src/public/components/register.js')
        }    
    },
    plugins: [
        // 配置全局標(biāo)識(shí)
        new webpack.DefinePlugin({ 
            "process.env": { NODE_ENV: JSON.stringify(process.env.NODE_ENV || "development") }
        }),
        new webpack.LoaderOptionsPlugin({
            debug: !isPro
        }),
        // 當(dāng)腳本有引入以下變量時(shí)媳叨,會(huì)自動(dòng)加載引入相對(duì)應(yīng)的模塊
        new webpack.ProvidePlugin({
            $: "jquery",// 一定要安裝低于2.0版本的,否則ie8會(huì)報(bào)錯(cuò)
            jQuery: "jquery",
            "window.jQuery": "jquery",
            React: 'react',
            ReactDOM: 'react-dom'
        }),
        // 抽離樣式
        new ExtractTextPlugin({
            filename:  (getPath) => {
                return getPath('css/style.css');
            },
            allChunks: true
        }),
        // 拷貝資源
        new CopyWebpackPlugin(
            [{
                from: './src/public/plugins/',
                to: 'plugins'
            }]
        ),
        // 提取被引用兩次以上的公共部分
        // new CommonsChunkPlugin({
        //     name:"chunk",
        //     minChunks:2,
        //     // minChunks: Infinity //提取所有entry依賴模塊
        // }),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'chunk',
            chunks: ['vendor2', 'bundle']
        }),

        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    ],
};

// 遍歷所有模板中的.html文件关顷,使用HtmlWebpackPlugin引入靜態(tài)資源(或者用ejs模板生成多個(gè))
var htmlfiles = fs.readdirSync(path.resolve(__dirname, "./src/public/templates"));
htmlfiles.forEach(function(item) {
    var currentpath = path.resolve(__dirname+"/src/public/templates", item);
    var extname = path.extname(currentpath);
    if(fs.statSync(currentpath).isFile() && (extname == ".html" || extname == ".ejs")) {
        var json = {
            template: currentpath, // 設(shè)置引入的模板
            filename: path.join(__dirname, './src/views/'+item), // 設(shè)置輸出路徑
            inject: 'body', // 設(shè)置js插入位置
            hash: true, // 為所有的資源加上hash值
            chunks: ["style", "vendor1", "vendor2", "common", "chunk", "bundle"], // 指定引入的資源
            showErrors: true // 是否展示錯(cuò)誤
        }
        // 生產(chǎn)環(huán)境壓縮html
        if(isPro) {
            json["minify"] = {
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
            }
        }
        config.plugins.push(
            new HtmlWebpackPlugin(json)
        );
    }
});

if(isPro) {
    config.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            mangle: {
                except: ['jQuery', '$', 'exports', 'require', "module"]
            }
        })
    );
}

module.exports = config;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糊秆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子解寝,更是在濱河造成了極大的恐慌扩然,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聋伦,死亡現(xiàn)場(chǎng)離奇詭異夫偶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)觉增,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門兵拢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人逾礁,你說(shuō)我怎么就攤上這事说铃。” “怎么了嘹履?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵腻扇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我砾嫉,道長(zhǎng)幼苛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任焕刮,我火速辦了婚禮舶沿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘配并。我一直安慰自己括荡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布溉旋。 她就那樣靜靜地躺著畸冲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上召夹,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天岩喷,我揣著相機(jī)與錄音,去河邊找鬼监憎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛婶溯,可吹牛的內(nèi)容都是我干的鲸阔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼迄委,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼褐筛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叙身,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渔扎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后信轿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晃痴,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年财忽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倘核。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡即彪,死狀恐怖紧唱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隶校,我是刑警寧澤漏益,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站深胳,受9級(jí)特大地震影響绰疤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稠屠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一峦睡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧权埠,春花似錦榨了、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春转捕,著一層夾襖步出監(jiān)牢的瞬間作岖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工五芝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痘儡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓枢步,卻偏偏與公主長(zhǎng)得像沉删,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子醉途,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • GitChat技術(shù)雜談 前言 本文較長(zhǎng)矾瑰,為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack隘擎,它要...
    蕭玄辭閱讀 12,693評(píng)論 7 110
  • 前兩部分我們已經(jīng)完成了博客頁(yè)面的展示和后臺(tái)頁(yè)面的展示: React技術(shù)棧+Express+Mongodb實(shí)現(xiàn)個(gè)人博...
    SamDing閱讀 5,463評(píng)論 1 12
  • 構(gòu)建一個(gè)小項(xiàng)目——FlyBird殴穴,學(xué)習(xí)webpack和react。(本文成文于2017/2/25) 從webpac...
    布蕾布蕾閱讀 16,821評(píng)論 31 98
  • 無(wú)意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺货葬,特此分享以備自己日后查看采幌,也希望更多的人看到...
    小小字符閱讀 8,164評(píng)論 7 35
  • 我想 黑暗中有一個(gè)獨(dú)屬于我的騎士 在我悲傷無(wú)助的時(shí)候 他會(huì)安靜的陪伴我 如靜寂的夜 安靜的立在那里 他不一定要有俊...
    墩墩不胖閱讀 149評(píng)論 0 2