npm+webpack+react+es6开发

前端技术框架
如今的前端技术日趋成熟,再也不是以前html+js+css的时代了。
当然,比起后端的成熟,前端还处于一个混乱时期,但是很多技术开始崭露头角。基本的思想也是越来越趋向于面向组件化,
版本化,可复用性等后端的思想。也许未来,前端后端又和在一起了,想起了很久以前的swing,确实怀念。

历史
所谓前端,就是用户直接接触的东西,直接面对用户。早期的html只是个标记,并不能动态展示,后来加入js脚本语言,可以
做些动态的东西,然后css也统一了样式风格,似乎一切都美好起来了。但是,大多数的框架还是动态生成html页面,类似JSP,ASP,
PHP这些,都是由后端动态生成html,然后浏览器解读。这样的问题就是,后端的数据问题和前端的页面美化问题纠缠在一起。
ajax是个革新者,不再一页一页的动态生成前端页面,而是局部更新数据。当然,无论如何,浏览器这个障碍确实是无法跨越。
各种浏览器,很难统一兼容,前端开发者还是需要操作各种浏览器的ODM元素。jquery极大简化了对DOM的操作,所以一下火了起来,
几乎无所不用。前端技术不断发展,事实上,与后端一样,我们都想一劳永逸,避开那些重复的体力劳动。这就是我们的目标是:
可复用,可扩展,可阅读,可维护……

目标
复用是最迫切的渴望,至少能够做代码搬运工。扩展可不是在原有代码上改动,而是新增的方式。可读性,也是入门门槛,可读性强的代码
至少能够使人心情舒畅。可维护,就是以上几点都加上,那么维护起来就很惬意。

如何做

安装node 6.3
好了,废话太多,直接切入主题,后端的版本依赖,打包问题,我们有maven,gradle等工具,这些工具使我们清晰的了解一个项目。
并且在测试,部署,等等问题上都可以很好的集成。那么前端呢?nodejs提供了npm,这里可以看成是maven一样的工具。
首先下载https://nodejs.org/dist/v6.9.1/node-v6.9.1-x64.msi  (linux版本请到主页下载 https://nodejs.org/en/ )
安装完成,请在命令行执行
C:\node --version
v6.3.0
C:\npm --version
3.10.3
 
npm设置
这里只需要配置下远程的库地址
使用以下命令
npm config set registry https://registry.npm.taobao.org/
 
这里实际上可以做私有的库,使用的是国内的淘宝镜像。只是为了快点。
首次使用,在webapp目录下,使用
npm install
 
会自动安装所有模块到 node_modules 目录下。这个目录的东西请不要提交到git

安装webpack
webpack 是用来打包的,当然打包过程中也对代码做了转换,所以我们可以使用es6,而最后再es5环境中执行。具体的webpack,下面
会说到。但是首先,先安装。
npm install webpack -g
 
g 表示全局

webpack安装好以后,使用起来也是很方便,
如果需要调试,可以使用
webpack -d
 
d会生成map文件,调试需要,映射原文件

如果上线,可用
webpack -p
 
如果需要动态调试,可以加上
webpack --watch -d
 
watch选项动态监控文件改变。有改变自动重新打包。

目录结构
工具好了,那么他们在目录中是一个什么样的结构呢?

* --build
 * --common.js
 * --common.js.map
 * --main.js
 * --main.js.map
 * --
* --css
 * --base.css
* --node_modules
* --src
 * --main.js
* --index.html
* --package.json
* --webpack.config.js

build是打包后的最终目录,如果使用了d选项,那么我们可以看到map文件。
css目录存放css文件
node_modules目录下,是安装的各种组件。执行npm install时候安装的
src则是存放我们源码的地方
index.html可以理解成入口文件,最终加载我们的成果的。
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <link href="build/main.css" rel="stylesheet" type="text/css">
    <link href="build/common.css" rel="stylesheet" type="text/css">
    <title>标题</title>
</head>
<body>
    <div id="wrap"></div>
</body>
<script src="./build/common.js"></script>
<script src="./build/main.js"></script>
</html>
 
package.json 打开发现很像maven的pom文件
{
  "name": "webpk",
  "version": "1.0.0",
  "description": "mytest",
  "main": "index.js",
  "scripts": {
    "test": "test"
  },
  "repository": {
    "type": "git",
    "url": ".."
  },
  "keywords": [
    "test"
  ],
  "author": "yinsx",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^1.13.3"
  },
  "dependencies": { 
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7"
  }
}
 
此文件可以用命令生成
npm init
 
接下根据提示一步一步完成即可。

最后是这个文件webpack.config.js,如果只是打包,估计也没啥讲的。
这里的配置,主要告诉webpack,打什么包,如何打包,同时如何处理各种文件(js,css,icon等)。
这里给个简易配置
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
    entry: {
        "main":"./src/main.js"
    },
    output: {
        path:'./build',
        filename:"[name].js"
    },
    module:{
        loaders:[
            {test:/\.jsx$/,loader:'babel-loader!jsx-loader?harmony'},
            //css load
            {test:/\.css$/,loader:ExtractTextPlugin.extract("style-loader","css-loader")},
            {test:/\.js$/,
                loader:'babel',
                query:{
                    cacheDirectory:true,
                    plugins:[
                        "transform-decorators-legacy",
                        "transform-class-properties",
                    ],
                    presets:["es2015","react"]}
            },
            {test:/\.(png|jpg)$/,loader:'url-loader?limit=8192'}
        ]
    },
    resolve:{
        extensions:["",".js",".jsx",".json"],
        alias: {
                    jquery: "jquery/src/jquery"
                }
    },
    plugins:[
        new ExtractTextPlugin("[name].css"),
        new CommonsChunkPlugin({
            names:['common'],
            minChunks:Infinity
        })
    ],
};
 
webpack.config.js 配置
在此过程中,我们会遇到很多问题。
第一句 require("webpack")这个webpack从何而来呢?
这就是我们开始安装的webpack,可以使用命令
npm install url-loader -save|-save-dev
 
这里save或者save-dev会把依赖存入package.json中
安装好这些组件,就可以在配置的时候使用了。
var ExtractTextPlugin = require("extract-text-webpack-plugin");
 
这句,引入一个外部插件,主要作用是把css文件独立作为.css,不打包到脚本中。这样可以使用link引用,而不是使用
style内嵌。
在plugins里,我们看到使用方法(创建):
pulugins:[
new ExtractTextPlugin("[name].css")
]
 
使用
module{
loaders:[
{test:/\.css$/,loader:ExtractTextPlugin.extract("style-loader","css-loader")},
]
}
 
CommonsChunkPlugin 这个插件则是用来提取公共的模块。这样我们能够控制把相似的模块打到一个包中。
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
    entry: {
        p1: "./page1",
        p2: "./page2",
        p3: "./page3",
        ap1: "./admin/page1",
        ap2: "./admin/page2"
    },
    output: {
        filename: "[name].js"
    },
    plugins: [
        new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
        new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
    ]
};
 
entry 是入口文件(输入)可以是一个对象。
output是输出文件。
module则是各种加载器用于加载不同的语法语言
从module我们看到,加载css的,加载图片的,还有加载jsx的,加载js的
这里的加载js比较复杂。
module:{
        loaders:[
            {test:/\.jsx$/,loader:'babel-loader!jsx-loader?harmony'},
            //css load
            {test:/\.css$/,loader:ExtractTextPlugin.extract("style-loader","css-loader")},
            {test:/\.js$/,
                loader:'babel',
                query:{
                    cacheDirectory:true,
                    plugins:[
                        "transform-decorators-legacy",
                        "transform-class-properties",
                    ],
                    presets:["es2015","react"]}
            },
            {test:/\.(png|jpg)$/,loader:'url-loader?limit=8192'}
        ]
    },
 
babel是个什么东西呢?其实就是一个转化器,他把ES6的语法转化成ES5,query是获取加载器的参数。曾经在这里坑了很久,
就是因为,忽略了版本问题,网上很多都是babel5的配置,而我的版本却是 babel6
* babel6
"babel-core":"6.18.2",
    "babel-loader":"6.2.7",
 
* babel 5
"babel-core": "^5.8.25",
 "babel-loader": "^5.3.2",
 
对于6的配置,可以参考前面的配置,对于5,则可以参考一下配置
module: {
        loaders: [{
            test: /\.js$/,
            loader: 'babel-loader',
            query: {
                optional: ["es7.decorators", "es7.classProperties"]
            }
        }, {
            test: /\.jsx$/,
            loader: 'babel-loader!jsx-loader?harmony'
        },{test:/\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader")},
        {test: /\.scss$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")},
        {test: /\.less$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")},
        { test: /\.(png|jpg|gif)$/, loader: 'url-loader?name=img/[hash:8].[name].[ext]' },
            { test: /\.(eot|ttf|svg|woff)$/, loader: 'url-loader?name=font/[hash:8].[name].[ext]' }]
    },
 
resolve 其实简化了我们的后缀,也就是不需要写后缀。比如 require("abc")
使用别名alias也可以配置搜索路径,jquery就是一个独特的路径
module.exports = {
  resolve: {
    alias: {
      js: path.join(__dirname, "src/scripts"),
      src: path.join(__dirname, "src/scripts"),
      styles: path.join(__dirname, "src/styles"),
      img: path.join(__dirname, "src/img")
    },
    root: [
      path.join(__dirname, "bower_components")
    ]
  },
  plugins: [
    new webpack.ResolverPlugin(
        new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"])
      )
  ]
}
 

好了,到目前,基本的配置就完成了。那么下面是我们如何开发呢?

编写代码

代码编写,最终有个html页面加载我们开发的页面。
还记得index.html吗?
里面有个div id="wrap"
这里就是入口的关键,看我们的代码src/main.js
import React,{ Component } from 'react';
import ReactDom from 'react-dom';

require("../css/base.css");

class Frame extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return <div className="frame">
            <div className='header'>{this.props.contents}</div>
        </div>
    }
};

class Page extends Component {
    constructor(props) {
        super(props);
    }
    render(){
        return <div className="homePage">
                <Frame contents="longen1">this is comment </Frame>
                <Frame contents="logen2"> this is another comment </Frame>
                </div>
    }
};
export default Page;

ReactDom.render(<Page></Page>,document.getElementById("wrap"));
 

如果你是写java的,或者c# C++的都应该会有亲切感吧。这就是后端
化的前台代码。
在这个过程中,如果找不到相应的组件,请手动安装
npm install xxx
 
如果只是开发打包或编译的时候需要,那么--save-dev即可,如果是上线后用,则--save
执行了save后,可以再package.json中看到相应的项,这比maven的pom方便多了。

最后一句
ReactDom.render(<Page></Page>,document.getElementById("wrap"));
 
实现了与开始的index.html对接

接下来,就是要学习react,ES6的语法了。