前言

  前幾天做了一個(gè)項(xiàng)目:【node】記錄項(xiàng)目的開始與完成——pipeline_kafka流式數(shù)據(jù)庫(kù)管理項(xiàng)目;因?yàn)殚_發(fā)時(shí)間緊迫,淺略的使用了一下react,感覺這個(gè)ui庫(kù)非常的符合我的口味,現(xiàn)在趁著有空閑時(shí)間,將項(xiàng)目前端重構(gòu)一番。這里面存在一些坑,都是深不見底的水坑,說多了都是淚水。。。好在順利完成,現(xiàn)在在這里再一步一步重來一遍,和需要學(xué)習(xí)webpack的前端童鞋分享。

準(zhǔn)備

tips:文章最后可下載demo

一:目錄

  

  首先我們要新建目錄,

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  新建app文件夾,它存放入口文件,component組件,

  新建static文件夾存放打包后的文件,

  新建webpack文件夾,存放的webpack配置文件。

二、生成package.json

  在當(dāng)前目錄打開cmd或者PowerShell或者終端,

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  輸入npm init 然后一直回車到執(zhí)行完畢,package.json就滾到文件夾根目錄下面了

 三、安裝webpack

  shell輸入 webpack i webpack --save 安裝webpack

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  我成功后的提示是這樣的。

四、新建文件

  在目錄app中新建main.js,

  在目錄webpack中新建webpack的配置文件  webpack.config.js

  在目錄static中新建一個(gè)  index.html

  在目錄static中新建一個(gè)js目錄

五、初步配置

  首先寫一下 /static/index.html這個(gè)文件,因?yàn)樯傻奈募夸浽?/static/js文件夾里面,所以這里要預(yù)先引用打包后的文件,最后訪問這個(gè)html文件就可以看到效果

  

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

<!DOCTYPE html><html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title>webpack_Demo</title>
    </head>
    <body>
        <div class="content">
        </div>
        <script src="./js/app.js" type="text/javascript" charset="utf-8"></script>
    </body></html>

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  然后配置webpack.config.js:

  剛開始我們就配一個(gè)簡(jiǎn)單的入口和輸出目錄就可以了: 

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

var path = require("path");

module.exports = {
    entry:{  //入口文件        "app":path.join(__dirname,"../app/main.js")  //app對(duì)應(yīng)生成的文件名
    },
    output:{
        path:path.join(__dirname,"../static/js/"),
        filename:"[name].js"   //這里[name]就是表示對(duì)應(yīng)entry對(duì)象的name,然后生成的后戳是.js
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  現(xiàn)在我們就可以簡(jiǎn)單的測(cè)試一下,在/app/main.js中隨意寫一些代碼 ,比如alert(1)。

  在shell中調(diào)用webpack測(cè)試,運(yùn)行:

  webpack --config ./webpack/webpack.config.js

   iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  成功后訪問index.html,查看效果,如果報(bào)錯(cuò),可能是缺少哪個(gè)依賴包,安裝后重復(fù)上面步驟。

  這個(gè)時(shí)候文件目錄就變成了:

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn) app.js就是打包后生成的文件。

  現(xiàn)在我們要把打包命令放在package.json里,因?yàn)槊看尉幾g都要運(yùn)行那么長(zhǎng)的命令,太痛苦了。在package.json簡(jiǎn)單的配置一個(gè)script就可以使用npm run xxx運(yùn)行了,編輯package.json:

  

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

{  "name": "web_pack",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1",    "build":"webpack --config ./webpack/webpack.config.js" //添加一個(gè)build 值是打包用到的命令
  },  "author": "",  "license": "ISC",  "dependencies": {    "webpack": "^3.0.0"
  }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

現(xiàn)在在shell里運(yùn)行 npm run build就可以編譯了

到這一步,初步的配置就算是完成了,接下來配置一個(gè)webpack-dev-server,然后就可以配置react組件,生成項(xiàng)目了

 

配置webpack-dev-server 

  什么是webpack-dev-server? 

  它是用來監(jiān)控文件的修改事件,啟動(dòng)它的時(shí)候,會(huì)分配一個(gè)端口,指向當(dāng)前的目錄,一旦目錄下文件被修改,它會(huì)通知瀏覽器自動(dòng)刷新頁面,省去了不斷的按f5的煩惱。

  在shell中安裝,運(yùn)行:

  npm i webpack-dev-server --save-dev

  安裝成功后,運(yùn)行:

  node_modules/.bin/webpack-dev-server --config ./webpack/webpack.config.js --port 8089 --open

  --config 是webpack配置文件目錄,--port 是運(yùn)行的端口號(hào) 

  運(yùn)行成功會(huì)在系統(tǒng)默認(rèn)瀏覽器彈出一個(gè)窗口,這是一個(gè)選擇文件夾的界面,我們?cè)L問static,就可以訪問到index.html了:

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  這里需要注意的一點(diǎn)是,你還需要將index.html中的app.js引用改成http://localhost:8089/app.js,因?yàn)閣ebpack-dev-server加載的是虛擬文件,目錄在根目錄下,所以需要修改這里?! ?/p>

<script src="http://localhost:8089/app.js" type="text/javascript" charset="utf-8"></script>

  項(xiàng)目完成后再改成對(duì)應(yīng)的路徑。

  修改完成,修改一下main.js中的js代碼,再看看瀏覽器中頁面是否改變。webpack-dev-server大致就配置好了,另外附上它的api地址:

  webpack-dev-server

 最后再把命令配置到package.json中

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

{  "name": "web_pack",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "node_modules\.bin\webpack-dev-server --config ./webpack/webpack.config.js --port 8089 --open",  //添加到test中 
    "build":"webpack --config ./webpack/webpack.config.js"   },  "author": "",  "license": "ISC",  "dependencies": {    "webpack": "^3.0.0"
  }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

現(xiàn)在運(yùn)行npm run test 就可以開始開發(fā)了。

 

配置react

  到了這一步,我們要開始配置react,首先還是安裝依賴包,編譯jsx文件需要用到babel、babel-core、babel-loader、babel-preset-es2015、babel-preset-react、還有react自己的react、react-dom。安裝他們:

  npm i babel babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom --save

  安裝成功之后,先在main.js寫一個(gè)demo

  

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

var React = require("react");var ReactDOM = require("react-dom");
ReactDOM.render(        <div>
            <h1>welcome</h1>
        </div>
        ,document.querySelector(".content")
    );

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  這里一定要用 ReactDOM.render 老的版本react.render會(huì)報(bào)語法錯(cuò)誤。

配置babel-loader 

  在webpack.config.js添加babel-loader用來解析jsx和es6的代碼:

  

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

var path = require("path");

module.exports = {
    entry:{        "app":path.join(__dirname,"../app/main.js") //入口文件 name對(duì)應(yīng)輸出的[name]    },
    output:{
        path:path.join(__dirname,"../static/js/"), //輸出路徑
        filename:"[name].js"        //輸出app.js    },
    module:{
        loader:[
            {
                test:/\.(js|jsx)$/,    //這是配置加載文件的規(guī)則 值是正則表達(dá)式 這里寫的意思是.js .jsx結(jié)尾的文件加載loader
                loader:"babel-loader",
                exclude:/node_module/,        //這個(gè)目錄不需要加載loader                query:{
                    presets:["react","es2015"]        //加載loader的presets                }
            }
        ]
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  配置了module下面的loaders。

  然后npm run test 開啟服務(wù),測(cè)試一下代碼是否可以運(yùn)行。

  順利的話,這里應(yīng)該能看到welcome了:

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

  現(xiàn)在的代碼結(jié)構(gòu)顯然過于簡(jiǎn)單,下面我們?cè)赼pp文件目錄下新建兩個(gè)目錄 component 和 views。

  先在component 文件夾下新建hello.jsx,寫一些代碼:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

import React from "react";class Hello extends React.Component{
    constructor(props){
        super(props);        this.propTypes = {
            text:React.PropTypes.string
        }
    }
    render(){        return <div className="hello">
                    hello <span>{this.props.text}</span>
                </div>
    }
}
module.exports = Hello;

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  一般我的import 語法用來表示react組件或其他資源的引入,require語法用來表示js的引入,另外這里用的是es6的class語法。關(guān)于es6,在文章下面有淺略說明。

  然后再views新建index.jsx,這里可以寫主要的頁面代碼:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

import React from 'react';
import Hello from "../component/hello.jsx"; //引入hello組件class Index extends React.Component{
    render(){        return <div className="index_container">
                    <Hello text="world"></Hello>
                </div>
    }
}

module.exports = Index;

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 最后main.js引入index,把index加入rander就可以看到效果了:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

var React = require("react");var ReactDOM = require("react-dom");

import Index from "./views/index.jsx"; //引入index
ReactDOM.render(        <div>
            <Index></Index>  //插入index視圖
        </div>
        ,document.querySelector(".content")
    );

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

查看效果:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

到了這一步,基本的視圖結(jié)構(gòu)就完成了,接下來配置css的加載,基本和react的方式一樣,加載對(duì)應(yīng)的loader,解析對(duì)應(yīng)的文件。

 

 

 

配置css:

  視圖解決了,現(xiàn)在我們要解決css的引入問題,這里可以選擇的就比較多 css、sass、less等都可以,我選擇的是css,因?yàn)槲业膕ass文件可以使用ruby的sass編譯,在編輯保存時(shí)已經(jīng)自動(dòng)編譯成css了,個(gè)人覺得這樣管理起來更加方便。

  首先還是安裝依賴,運(yùn)行:

  npm i css-loader file-loader style-loader url-loader --save-dev

  file-loader url-loader 是用來編譯圖片資源的,它會(huì)將url()中的靜態(tài)資源打包編譯成base64格式,這里需要注意,不要在css中寫找不到文件的路徑,否則會(huì)報(bào)錯(cuò)編譯失敗。

  安裝完成后配置css-loader:

  

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 path = require(=:path.join(__dirname,) ), 
        filename:        /\.(js|jsx)$/,    
                loader:/node_module/,        ,]        /\.css$/

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

注意style-loader必須寫在css-loader前面,否則就會(huì)報(bào)錯(cuò)

  配置完成,在hello.jsx引入個(gè)css瞧瞧(這里要重啟一下test):

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 引入的方法是

import "./hello.css";

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

引入成功,這樣css就可以使用了

 

項(xiàng)目結(jié)構(gòu)

  到目前為止 項(xiàng)目的結(jié)構(gòu)是這樣的:

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  app中存放 組件(component)、視圖(views)、入口文件 。

  static中存放打包后的項(xiàng)目文件。

  webpack中存放webpack的配置文件

  建議將公共組件打包,比如建一個(gè)hello文件夾,里面存放hello.jsx和hello.css以及需要的插件、文檔,這樣它的多項(xiàng)目復(fù)用將變得非常方便,拷貝文件即可。

淺談es6以及react中的小坑

  es6的class關(guān)鍵字看起來很性感,實(shí)際上也只是整了個(gè)容,感覺內(nèi)在變化不多。與createClass差別不是很大,在react中每次生命組件都要繼承React.Component。

  比如上面的hello.jsx:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

constructor(props){
        super(props);        this.propTypes = {
            text:React.PropTypes.string
        }
    }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  這是構(gòu)造函數(shù),就是new xxx()調(diào)用的那個(gè)函數(shù),這里為啥要super? 這是個(gè)奇怪的寫法,原因是因?yàn)闃?gòu)造函數(shù)中訪問不到this,需要調(diào)用super()才能順利訪問到this,這里個(gè)人尤為不解,雖然理解起來也不難,構(gòu)造函數(shù)屬于static方法,這里為啥不能和java一樣的邏輯,構(gòu)造函數(shù)只能訪問類?

   this.propTypes  

   這個(gè)屬性標(biāo)明了組件中所有用到的props 并且能驗(yàn)證傳入的值的正確性,感覺組件有它非常有必要,建議寫props不要少了這個(gè)屬性。

 react 中this的坑

  在hello.jsx組件上添加一個(gè)click事件,在事件中打印this:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

class Hello extends React.Component{
    constructor(props){
        super(props);        this.propTypes = {
            text:React.PropTypes.string
        }
    }
    render(){        return <div className="hello" onClick={this.print}>
                    hello <span>{this.props.text}</span>
                </div>
    }
    print(){
        console.log("點(diǎn)擊事件");
        console.log(this);
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

   會(huì)發(fā)現(xiàn)this的值既不是點(diǎn)擊dom也不是class而是null:

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  可是如果在靜態(tài)文件中寫React.createClass則不會(huì)出現(xiàn)null。

  我的解決辦法是在render中強(qiáng)制指定this為class: 

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

render(){        this.print = this.print.bind(this);//綁定this到函數(shù)        return <div className="hello" onClick={this.print}>
                    hello <span>{this.props.text}</span>
                </div>
    }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

查看官方文檔解釋是:

  React.createClass有一個(gè)內(nèi)置的魔術(shù)功能,可以自動(dòng)綁定所有方法this。這對(duì)于在其他類中不用于此功能的JavaScript開發(fā)人員來說可能會(huì)有點(diǎn)混亂,或者當(dāng)從React轉(zhuǎn)到其他類時(shí)可能會(huì)令人困惑。因此,我們決定不將此內(nèi)置到React的類模型中。如果需要,您仍然可以在構(gòu)造函數(shù)中明確地預(yù)處理方法。

 

在遇到點(diǎn)擊更改state時(shí),一定會(huì)用到this。要注意this的指向問題

總結(jié)

  前端就像是一只正當(dāng)壯年的母雞,今天下個(gè)蛋,明天下個(gè)蛋,是寡蛋是好蛋不知道,今天的蛋和明天的蛋嘗起來味道也差不多。我們就像是飼養(yǎng)員,負(fù)責(zé)給母雞送吃的,下了蛋就要馬上負(fù)責(zé)收,否則要么就爛了、要么就孵出小雞吃不了雞蛋了。

  那些層出不窮的框架,壞掉的框架、已經(jīng)變的強(qiáng)大就很難駕馭的框架,這對(duì)前端來說是一個(gè)考驗(yàn)。

相關(guān)資源

http://www.cnblogs.com/ztfjs/p/webpack.html