天天看點

【前端知識點】webpack 打包 + es6 + react入門(一)webpack打包webpack 打包 + es6 + react入門【第一部分】Webpack入門【第二部分】ES6新特性

webpack 打包 + es6 + react入門

本文主要針對webpack打包流程,注意事項,es6新特性變化,以及es6 開發react的入門教程:

  • webpack打包流程
  • webpack注意事項
  • webpack,gulp,grunt差異對比
  • webpack進階:關于加載優化與緩存
  • es6 新标準及新特性
  • es6 核心特性分析
  • react 基本文法
  • react 生命周期
  • react 父子元件的通信總結

【第一部分】Webpack入門

webpack打包流程

React開發和子產品管理的主流工具webpack也被大家所熟知。

web開發中常用到的靜态資源主要有JavaScript、CSS、圖檔、Jade等檔案,webpack中将靜态資源檔案稱之為子產品。webpack是一個module bundler(子產品打包工具),其可以相容多種js書寫規範,且可以處理子產品間的依賴關系,具有更強大的js子產品化的功能。

【前端知識點】webpack 打包 + es6 + react入門(一)webpack打包webpack 打包 + es6 + react入門【第一部分】Webpack入門【第二部分】ES6新特性

webpack特性

webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:

序号 特點 備注
1 對 CommonJS 、 AMD 、ES6的文法做了相容 相容性強
2 對js、css、圖檔等資源檔案都支援打包 打包範圍廣
3 串聯式子產品加載器以及插件機制,讓其具有更好的靈活性和擴充性,例如提供對CoffeeScript、ES6的支援 靈活易擴充
4 有獨立的配置檔案webpack.config.js 獨立配置,高内聚
5 可以将代碼切割成不同的chunk,實作按需加載,降低了初始化時間 按需,高效,低耦合
6 支援 SourceUrls 和 SourceMaps,易于調試 [不很懂]就是即使代碼壓縮,也可以轉化為非混淆壓縮形式調試
7 具有強大的Plugin接口,大多是内部插件,使用起來比較靈活 靈活
8 webpack 使用異步 IO 并具有多級緩存。這使得 webpack 很快且在增量編譯上更加快 異步,緩存,性能優

1.相比Grunt,WebPack除了具備豐富的插件外,同時帶有一套加載(Loader)系統。使它支援多種規範的加載方式,包括ES6、CommonJS、AMD等方式,這是Grunt、Gulp所不具備的。

2.代碼混淆的角度來看,WebPack更加的極緻

3.代碼分片為處理單元(而不是檔案),使得檔案的分片更為靈活。

Webpack安裝和配置

1.安裝
webpack 可以作為 全局的npm子產品安裝,也可以在 目前項目中安裝。
npm install -g webpack //全局安裝
npm install --save-dev webpack //産品模式用dependencies,開發模式用devDep; save就是存到依賴清單裡package.json
           

全局安裝的webpack,直接執行webpack指令,會預設使用目前目錄的webpack.config.js作為配置檔案。如果要指定另外的配置檔案,可以執行:

webpack -config webpack.chauncey.config.js //指定新檔案
           
配置
每個項目下需配置一個webpack.config.js, 如同gulpfile.js/Gruntfile.js, 通俗一點,他就是你的一個初始化設定,配置項,告訴webpack,你想怎麼去處理你的檔案。

環境變量

通過NODE_ENV可以來設定環境變量(預設值為development)。區分開發和生産環境,對于檔案的不同處理。

linux & mac: export NODE_ENV=production

windows:set NODE_ENV=production

運作前可以設定目前環境,SET NODE_ENV=production(開發環境)

set PORT=1234設定接口

entry

entry參數: 打包的輸入檔案,參數值代表輸出後檔案形式,字元串(單個檔案)、數組(一大個檔案)、對象(不同屬性為不同檔案)

{
    entry: {
        page1: "./page1",
        //支援數組形式,将加載數組中的所有子產品,但以最後一個子產品作為輸出
        page2: ["./entry1", "./entry2"],
        page3: {
            subp1: "./sp1",
            subp2: "./sp2"
        }

    },
    output: {
        path: "dist/js/page",//目标目錄【檔案夾】
        publicPath: "/output/",
        filename: "[name].bundle.js"
    }
}
           

output

output參數為對象,定義了輸出檔案的位置及名字

output: {
        path: "dist/js/page",//打封包件存放的絕對路徑
        publicPath: "/output/",//網站運作時的通路路徑
        filename: "[name].bundle.js"//打包後的檔案名
    }
           

當我們在entry中定義建構多個檔案時,filename可以對應的更改為[name].js用于定義不同檔案建構後的名字.

module

在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖檔等靜态檔案都是子產品,不同子產品的加載是通過子產品加載器(webpack-loader)來統一管理的。loaders之間是可以串聯的,一個加載器的輸出可以作為下一個加載器的輸入,最終傳回到JavaScript上:

module: {
   //加載器配置
   loaders: [
       //.css 檔案使用 style-loader 和 css-loader 來處理
       { test: /\.css$/, loader: 'style-loader!css-loader' },

       //.js 檔案使用 jsx-loader 來編譯處理
       { test: /\.js$/, loader: 'jsx-loader?harmony' },

       //.scss 檔案使用 style-loader、css-loader 和 sass-loader 來編譯處理
       { test: /\.scss$/, loader: 'style!css!sass?sourceMap'},

       //圖檔檔案使用 url-loader 來處理,小于8kb的直接轉為base64
       { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
   ]
}
           

其中, test:表示比對的資源類型;

loader或loaders:表示用來加載這種類型資源的loader(加載器);[可參考using loaders]

! 用來定義loader的串聯關系,”-loader”是可以省略不寫的,多個loader之間用“!”連接配接起來,但所有的加載器都需要通過 npm 來加載。

此外,還可以添加用來定義png、jpg這樣的圖檔資源在小于10k時自動處理為base64圖檔的加載器:

{ test: /\.(png|jpg)$/,loader: 'url-loader?limit=10000'}
           

給css和less還有圖檔添加了loader之後,我們不僅可以像在node中那樣require js檔案了,我們還可以require css、less甚至圖檔檔案:

require('./bootstrap.css');
 require('./myapp.less');
 var img = document.createElement('img');
 img.src = require('./glyph.png');
           

注意,require()還支援在資源path前面指定loader,即require(![loaders list]![source path])形式:

require("!style!css!less!bootstrap/less/bootstrap.less");
// “bootstrap.less”這個資源會先被"less-loader"處理,
// 其結果又會被"css-loader"處理,接着是"style-loader"
// 可類比pipe操作
           

require()時指定的loader會覆寫配置檔案裡對應的loader配置項。

resolve

webpack在建構包的時候會按目錄的進行檔案的查找,resolve屬性中的extensions數組中用于配置程式可以自行補全檔案字尾:

resolve: {
    //查找module的話從這裡開始查找
    root: '/pomy/github/flux-example/src', //絕對路徑

    //自動擴充檔案字尾名,意味着我們require子產品可以省略不寫字尾名
    extensions: ['', '.js', '.json', '.scss'],

    //子產品别名定義,友善後續直接引用别名,無須多寫長長的位址
    alias: {
        AppStore : 'js/stores/AppStores.js',//後續直接 require('AppStore') 即可
        ActionType : 'js/actions/ActionType.js',
        AppAction : 'js/actions/AppAction.js'
    }
}
           

然後我們想要加載一個js檔案時,隻要require(‘common’)就可以加載common.js檔案了。

注意一下, extensions 第一個是空字元串! 對應不需要字尾的情況.

plugin

webpack提供了[豐富的元件]用來滿足不同的需求,當然我們也可以自行實作一個元件來滿足自己的需求:

plugins: [
     //your plugins list
 ]
           

在webpack中編寫js檔案時,可以通過require的方式引入其他的靜态資源,可通過loader對檔案自動解析并打封包件。通常會将js檔案打包合并,css檔案會在頁面的header中嵌入style的方式載入頁面。但開發過程中我們并不想将樣式打在腳本中,最好可以獨立生成css檔案,以外鍊的形式加載。這時extract-text-webpack-plugin插件可以幫我們達到想要的效果。需要使用npm的方式加載插件,然後參見下面的配置,就可以将js中的css檔案提取,并以指定的檔案名來進行加載。

plugins: [
    new ExtractTextPlugin('styles.css')
]
           

externals

當我們想在項目中require一些其他的外部類庫或者API,而又不想讓這些類庫的源碼被建構到運作時檔案中,這在實際開發中很有必要。此時我們就可以通過配置externals參數來解決這個問題:

externals: {
    "jquery": "jQuery"
}
           

這樣我們就可以放心的在項目中使用這些API了:var jQuery = require(“jquery”);

context

當我們在require一個子產品的時候,如果在require中包含變量,像這樣:

require("./mods/" + name + ".js");
           

那麼在編譯的時候我們是不能知道具體的子產品的。但這個時候,webpack也會為我們做些分析工作:

1.分析目錄:’./mods’;

2.提取正規表達式:’/^.*.js$/’;

于是這個時候為了更好地配合webpack進行編譯,我們可以給它指明路徑,像在cake-webpack-config中所做的那樣(我們在這裡先忽略abcoption的作用):

var currentBase = process.cwd();
 var context = abcOptions.options.context ? abcOptions.options.context : 
 path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);
           

關于 webpack.config.js 更詳盡的配置可以參考這裡

webpack常用指令

webpack的使用通常有三種方式:

1、指令行使用:webpack

var webpack = require('webpack');
webpack({
//configuration
}, function(err, stats){});
           

3、預設使用目前目錄的webpack.config.js作為配置檔案。如果要指定另外的配置檔案,可以執行:webpack –config webpack.custom.config.js

webpack 的執行也很簡單,直接執行

即可,後面的參數“–display-error-details”是推薦加上的,友善出錯時能查閱更詳盡的資訊(比如 webpack 尋找子產品的過程),進而更好定位到問題。

常用指令

webpack的使用和browserify有些類似,下面列舉幾個常用指令:

即可,後面的參數“--display-error-details”是推薦加上的,友善出錯時能查閱更詳盡的資訊(比如 webpack 尋找子產品的過程),進而更好定位到問題。
常用指令
webpack的使用和browserify有些類似,下面列舉幾個常用指令:
           

前面的四個指令比較基礎,使用頻率會比較大,後面的指令主要是用來定位打包時間較長的原因,友善改進配置檔案,提高打包效率。

圖檔打包和靜态資源伺服器

圖檔打包

webpack中對于圖檔的處理,可以通過url-loader來實作圖檔的壓縮。

div.img{
    background: url(../image/xxx.jpg)
}

//或者
var img = document.createElement("img");
img.src = require("../image/xxx.jpg");
document.body.appendChild(img);
           

針對上面的兩種使用方式,loader可以自動識别并處理。根據loader中的設定,webpack會将小于指點大小的檔案轉化成 base64 格式的 dataUrl,其他圖檔會做适當的壓縮并存放在指定目錄中。

module: {
    {
      test: /\.(png|jpg)$/,
      loader: 'url-loader?limit=10000&name=build/[name].[ext]'
    }]
}
           

對于上面的配置,如果圖檔資源小于10kb就會轉化成 base64 格式的 dataUrl,其他的圖檔會存放在build/檔案夾下。

靜态資源伺服器

除了提供子產品打包功能,Webpack還提供了一個基于Node.js Express架構的開發伺服器,它是一個靜态資源Web伺服器,對于簡單靜态頁面或者僅依賴于獨立服務的前端頁面,都可以直接使用這個開發伺服器進行開發。在開發過程中,開發伺服器會監聽每一個檔案的變化,進行實時打包,并且可以推送通知前端頁面代碼發生了變化,進而可以實作頁面的自動重新整理。

Webpack開發伺服器需要單獨安裝,同樣是通過npm進行:

可以使用webpack-dev-server直接啟動,也可以增加參數來擷取更多的功能,具體配置可以參見官方文檔。預設啟動端口8080,通過localhost:8080/webpack-dev-server/可以通路頁面,檔案修改後儲存時會在頁面頭部看到sever的狀态變化,并且會進行熱替換,實作頁面的自動重新整理。

Webpack注意事項-Tips

1.建議,首頁盡量做到最精簡,畢竟決定使用者存留時間。按需。

2.【推薦很好】http://www.cnblogs.com/giveiris/p/5237080.html

【前端建構】WebPack執行個體與前端性能優化

3.【有雪碧圖,gulp和webpack】【基于webpack搭建純靜态頁面型前端工程解決方案模闆】https://github.com/chemdemo/webpack-seed

4.所有資源都是子產品。

Webpack、gulp、grunt異同

webpack實踐

webpack安裝

npm install webpack -g
           

webpack配置

// webpack.config.js
var path = require("path");
module.exports = {
  entry: './public/js/entry.js', //示範單入口檔案
  output: {
    path: path.join(__dirname, './public/pro'),  //打包輸出的路徑
    filename: 'bundle.js',            //打包後的名字
    publicPath: "./pro"             //html引用路徑,在這裡是本地位址。
  }
};
           

編寫入口檔案

接下來就編寫我們的入口檔案 entry.js 和第一個子產品檔案 module1.js 。我們一切從簡,裡面隻用來加載一個Js子產品。

// entry.js
require("./module1"); // 使用CommonJs來加載子產品
           
// module1.js
console.log("hello,world! module1");
document.write("超哥威武");
           

啟動webpack

一切準備好後,我們僅需要在項目根目錄下,用指令行 webpack 執行一下即可。

// webpack 指令行的幾種基本指令

$ webpack // 最基本的啟動webpack方法
$ webpack -w // 提供watch方法,實時進行打包更新
$ webpack -p // 對打包後的檔案進行壓縮,提供production
$ webpack -d // 提供source map,友善調試。
           

多子產品依賴

跑通webpack打包entry,現在嘗試commonjs和amd,兩種子產品機制。

require(["./module3"], function(){
  console.log("hello,world! module1&&require(module3)");
  document.write("超哥威武");
});
           
// module2.js,使用的是CommonJs機制導出包
module.exports = function(a, b){
  return a + b;
}
           
// module3.js,使用AMD子產品機制
define(['./module2.js'], function(sum){
  return console.log("1 + 2 = " + sum(, ));
})
           

混用兩種不同機制非常不好,這裡僅僅是展示用的;

在開發新項目時還是推薦CommonJs或ES2015的Module。ES2015的子產品機制,是趨勢。【這一塊我會報個小錯誤,沒有完全實作】

【loader加載器】

預處理器做一些CoffeeScript 和 Sass 的編譯。我們以前要編譯這些預處理器,用 gulp相對複雜,webpack可以一次性解決!

在這裡我們用Sass和babel編譯ES2015為例子,看一下loader是如何使用的。

安裝loader

配置loader

// webpack.config.js
module.exports = {
  entry: path.join(__dirname, 'src/entry.js'),
  output: {
    path: path.join(__dirname, 'out'),
    publicPath: "./out/",
    filename: 'bundle.js'
  },
  // 新添加的module屬性
  module: {
    loaders: [
      {test: /\.js$/, loader: "babel"},
      {test: /\.css$/, loader: "style!css"},
      {test: /\.(jpg|png)$/, loader: "url?limit=8192"},
      {test: /\.scss$/, loader: "style!css!sass"}
    ]
  }
};
           

【webpack進階:關于加載優化與緩存,及版本控制】

1.uglify

webpack提供插件UglifyJsPlugin,可以優化(支援壓縮、混淆)代碼。

配置以下清單,在混淆代碼時,以下配置的變量,不會被混淆

new webpack.optimize.UglifyJsPlugin({
    mangle: {
        except: ['$super', '$', 'exports', 'require']
    }
})
           

以上變量‘ super′,‘ ’, ‘exports’ or ‘require’,不會被混淆

example

var UglifyJsPlugin = require("../../node_modules/webpack/lib/optimize/UglifyJsPlugin");

module.exports = {
    entry: "./entry.js",
    output: {
        path: __dirname,
        filename: "bundle.js",
    },
    plugins: [
        //使用醜化js插件
        new UglifyJsPlugin({
            compress: {
                warnings: false
            },
            mangle: {
                except: ['$scope', '$']
            }
        })
    ]
};
           
//entry.js
define("entry", function () {
    //變量 iabcdef 已引用,混淆
    var iabcdef = ;
    //變量 $scope 已引用,但不混淆
    var $scope = "scope";

    document.write("entry module" + iabcdef);
    document.write($scope);

    //變量 ixzy 未被引用,剔除
    var ixzy = ;
});
           

2.版本控制

對于靜态資源的版本控制,目前微信項目采取辦法是版本号作為請求參數,版本号為釋出日期,但有兩個問題:

1、更新版本時,CDN不能及時更新;

2、沒有發生變更的檔案也被賦上新版本

Webpack的做法是,生成hash,區分檔案。

配置方法

//所有代碼塊添加hash
module.exports = {
    entry: "./entry.js",
    output: {
        path: "assets/[hash]/",
        publicPath: "assets/[hash]/",
        filename: "bundle.js"
    }
};
           
【前端知識點】webpack 打包 + es6 + react入門(一)webpack打包webpack 打包 + es6 + react入門【第一部分】Webpack入門【第二部分】ES6新特性

生成單個代碼塊檔案的hash

配置方法

//單個代碼塊添加hash
module.exports = {
    entry: "./entry.js",
    output: {
        path: "build/",
        publicPath: "build/",
        chunkFilename: "[id].[hash].bundle.js",
        filename: "output.[hash].bundle.js",
    }
};
           

【關于:chunkFilename意義】

http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256

3.如何緩存 緩存控制要做到兩件事情,提到緩存命中率
對于沒有修改的檔案,從緩存中擷取檔案
對于已經修改的檔案,不要從緩存中擷取
圍繞這兩點,演繹出了很多方案,此處列兩種:
不處理,等待使用者浏覽器緩存過期,自動更新。這是最偷懶的,命中率低一些,同時可能會出現部分檔案沒有更新,導緻報錯的情況。
Http頭對檔案設定很大的max-age,例如1年。同時,給每個檔案命名上帶上該檔案的版本号,例如把檔案的hash值做為版本号,topic. ef8bed6c.js。即是讓檔案很長時間不過期。
當檔案沒有更新時,使用緩存的檔案自然不會出錯;
當檔案已經有更新時,其hash值必然改變,此時檔案名變了,自然不存在此檔案的緩存,于是浏覽器會去加載最新的檔案。
從上面的截圖可以看出來,通過WebPack是可以很輕松做到第二點的——隻需要給檔案名配置上[chunkhash:8]即可,其中8是指hash長度為8,預設是16。
output{
  path: _dirname + '/release/',
  filename: '[chunkhash:8].[name].js',
  chunkFilename: '[name].[chunkhash:8].js'
}
           

P.S.這樣的處理效果已經很好了,但同樣有劣處,即浏覽器給這種緩存方式的緩存容量太少了,隻有12Mb,且不分Host。是以更極緻的做法是以檔案名為Key,檔案内容為value,緩存在localStorage裡,命中則從緩存中取,不命中則去伺服器取,雖然緩存容量也隻有5Mb,但是每個Host是獨享這5Mb的。

4.自動生成頁面

檔案名帶上版本号後,每一次檔案變化,都需要Html檔案裡手動修改引用的檔案名,這種重複工作很瑣碎且容易出錯。html内部檔案的名字如何動态改變。

使用 HtmlWebpackPlugin 和 ExtractTextPlugin 插件可以解決此問題。

生成帶js的頁面

【參考】http://www.cnblogs.com/haogj/p/5160821.html

new HtmlWebpackPlugin({
        filename: 'index.html',
        template: __dirname + '/public/index.html',
        inject: true,//是否injection 
        chunks: ['page1'],//所要控制的子產品組

        //排序
        chunksSortMode: function(a, b){
            var index = {'page1': },
                aI = index[a.origins[].name],
                bI = index[b.origins[].name];
            return aI&&bI ? bI - aI : -;
        }
    }),
           

生成帶css的頁面

  new ExtractTextPlugin(“comm.[contenthash:9].css”)

  插件介紹到此為止,然而,還有一個關于同步加載和異步加載的問題,否則入口檔案還是會很臃腫。

5.同步/異步加載 一個原則是:首屏需要的同步加載,首屏過後才需要的則按需加載(異步)

同步的代碼會被合成并且打包在一起;

異步加載的代碼會被分片成一個個chunk,在需要該子產品時再加載,即按需加載。

同步加載過多代碼會造成檔案過大影響加載速度,異步過多則檔案太碎,造成過多的Http請求,同樣影響加載速度。

同步寫法

var TopicItem = require('../topic/topicitem');
           

異步寫法

require.ensure([], function(){
    //to do
});
           

【遺留問題】

  1. webpackJsonp is not defined

    是因為commonschunkplugin引用了,但是頁面沒用到。

  2. 路徑問題:

    entry: {

    page1: [‘./public/js/entry.js’], //示範單入口檔案 path.join(__dirname, ‘./public/pro’)

    page2: [‘./public/js/module1.js’]

    },

    路徑必須加./相對位址,否則會報錯publick/js**

3.待學習的:

1)webpack在PC項目中的應用

http://web.jobbole.com/85396/

2)基于 Webpack 和 ES6 打造 JavaScript 類庫

http://web.jobbole.com/84858/

3)webpack使用優化

http://web.jobbole.com/84847/

4)徹底解決 webpack 打封包件體積過大

http://www.jianshu.com/p/a64735eb0e2b

5)大公司裡怎樣開發和部署前端代碼

https://github.com/fouber/blog/issues/6

【第二部分】ES6新特性