【單頁面博客從前端到後端】環境搭建

工欲善其事,必先利其器。單頁面應用的開發和生產環境涉及文件的編譯、壓縮、打包、合併等,目前前端最流行的莫過於 webpack 。爲了深刻了解 webpack 以及其相關插件,咱們決定不採用腳手架,本身來搭建基於 webpack 的開發和生產環境。css

基礎環境

nodejs的安裝: 移步官網html

建議使用nvm來管理nodejs的版本
安裝nvm前端

Webpack相關plugin、loader的介紹

咱們使用的是 webpack@2.X.X ,建議讀完 官方文檔 對她有個大概的瞭解。html5

  • webpack-dev-serverwebpack-dev-server 來進行開發環境下面的自動打包編譯,包括熱更新等等。固然也能夠本身經過 webpack-dev-middleware 來自定義一個開發服務器。具體能夠參考 webpack-dev-server 的源代碼。node

  • webpack-hot-middleware 向入口文件中添加一個 client 文件,當文件變化時,服務器端能夠經過 socket 事件來通知這個 client 來實現熱更新。注:這個事件是 EventSource事件webpack-dev-server 已經將這個中間件封裝到內部,咱們只須要進行配置便可。react

  • babel-loader 編譯 es6 代碼。移步官網。值得一提的是 es2016, es2017, env 等是對已經或者將要被加入 JS這門語言的提案進行預編譯,而 stage-0 , stage-1 , stage-2 等是對未來可能加入立案裏面的語法的預編譯。webpack

  • extract-text-webpack-plugin 樣式文件默認會被 webpack 打包到js文件中。這個插件能夠提取出這些被打包進入的文件。git

固然咱們用到的不僅是這些,你能夠到npm官網或者github上面找到這些plugin、loader的詳細用法es6

初始目錄結構

blog 
├─ dist           # 輸出目錄
├─ task           # 這裏來放webpack處理和配置文件
├─ src
|  ├─ components  # 組件
|  └ index.js    # 入口文件
| package.json

跑通開發環境

src/ 目錄下新建入口文件 index.jsgithub

import React from 'react'
import ReactDOM from 'react-dom'
// 這裏須要藉助 webpack 的同名功能來代替繁瑣的相對路徑
import HomeComponent from 'components/Home'

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

src/component/ 目錄下新建 Home.js 模塊

import React, { Component } from 'react'

export default class Home extends Component {
    render(){
        return <div>Hello world!</div>
    }
}

src/ 目錄下新建 index.html 文件來做爲單頁面的HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>blog</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

如今咱們來添加 webpack 的配置文件來對這些文件進行打包、編譯

task/ 目錄下新建 config.js 文件

const path = require('path')
const webpack = require('webpack')
const webpackMerge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const base = {
    // 上下文環境,相對路徑都基於這個路徑
    context: path.resolve(__dirname, '..'),
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, '../dist'),
        publicPath: '/assets/',
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                include: path.resolve(__dirname, '../src'),
                exclude: [ /node_modules/ ],
                use: [
                    'react-hot-loader',
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['env', 'react'],
                            plugins: [
                                "add-module-exports",
                                "transform-runtime"
                            ]
                        }
                    }
                ],
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.jsx'],
        alias: {
            // 這裏對應着入口文件中 component 的同名配置
            components: path.resolve(__dirname, '../src/components')
        }
    }
}

const dev = webpackMerge(base, {
    output: {
        publicPath: '/'
    },
    // 源文件的 source map
    devtool: "source-map",
    plugins: [
          new webpack.HotModuleReplacementPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"development"'
            }
        }),
        new HtmlWebpackPlugin({
            
            template: './src/index.html',
            filename: 'index.html',
            inject: true
        })
    ],
    devServer: {
          // 開啓熱加載,須要 hmr 的支持
        hot: true,
        contentBase: path.resolve(__dirname, '../dist'),
          // 這個路徑必定要和 output 的 publicPath 的屬性一致
        publicPath: '/',
    }
})

const prod = webpackMerge(base, {

})

// 根據 NODE_ENV 來決定輸出的配置
module.exports = process.env.NODE_ENV === 'production' ? prod : dev

在項目跟路徑下執行

export NODE_ENV=development && webpack-dev-server --config ./task/config.js

export NODE_ENV=development 設置 NODE_ENV 這個環境變量爲 development 有助於咱們區分開發環境和生產環境。這是mac下面的設置方法,windows 能夠自行搜索

編譯成功,而且頁面輸出 Hello world! 表示配置跑通...
package.json 添加

{
    "scripts": {
        "dev": "export NODE_ENV=development && webpack-dev-server --config ./task/config.js --progress --colors --hotOnly"
    }
}

TROUBLESHOOTING

經過 webpack-merge 來覆蓋 output.publicPath 屬性

若是 devServer.publicPath = output.publicPath = '/assets/' 的話,那麼在瀏覽器中打開 localhost:80XX/assets/index.html 才能訪問到 index.html 文件,而將 publicPath = '/' 就直接經過 localhost:80XX 就能夠訪問。

loader 的配置

其中 include, exclude 就很少說

{
    use: [
        'react-hot-loader',
        {
            loader: 'babel-loader',
            options: {
                presets: ['env', 'react'],
                plugins: [
                    "add-module-exports",
                    "transform-runtime"
                ]
            }
        }
    ],
}

react-hot-loader 會解決更改 react 組件的時 webpack 熱更新直接刷新頁面的問題。

babel-presets-react 用來處理 react 的 jsx 語法

babel-plugin-add-module-exports babel@6 會將es6的語法

// home.js
export default 'foo'

轉化爲

'use strict';
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = 'foo';

因此在使用 commonjs 的語法 require('./home') 時,獲得的是
{default: 'foo'} ,因此:

var home = require('./home').default
console.log(home) // 'foo'

這個插件能夠避免這一現象

html-webpack-plugin

這裏解釋一下使用 html-webpack-plugin 的必要性。
其實徹底能夠扔一個靜態的 index.htmlwebpack-dev-server ,這裏面只須要有個 <script src="bundle.js"></script> 標籤,就能夠成功來進行訪問。可是更多的時候咱們會定製打包文件的名稱: output.filename: '[name]-[hash].js' ,編譯以後 webpack 會輸出一個相似於這樣的文件 bundle-fb9758acf17b2b5fb653.js ,那麼你每次打包都須要去更改那個src屬性,而 html-webpack-plugin 能夠幫你解決這些事情

添加對樣式文件的支持

src/component/ 下新建 Home.module.less

@color: green;

:global {
    body {
        background-color: red;
    }
}

.wrap {
    color: @color;
}

在同級目錄下的 Home.js 組件中引入這個 less 文件

import React, { Component } from 'react'
import Style from './home.module.less'

export default class Home extends Component {
    render(){
        return <div className={Style.wrap}>Hello world!</div>
    }
}

咱們沒有在 task/config.js 下增長對 less 文件的支持,確定會報錯的。先說一下爲何要以 .module.less 標識 less 文件,只會咱們會引入 babel-plugin-import 對樣式庫 antd 進行按需加載,因爲 antd 源代碼中的樣式文件也是用 less 寫的,這樣會致使這些文件被做爲 css-module 處理,因此加以區別,這是參考 atool-build 的配置。

task/config.js 文件新增 :

const ExtractTextPlugin = require('extract-text-webpack-plugin')
const autoprefixer = require('autoprefixer')
const runsack = require('rucksack-css')
const theme = require('../theme.js')()
const postcssPlugins = () => [
    runsack(),  // 可選
    autoprefixer({
        browsers: ['last 2 versions', 'Firefox ESR', '> 1%', 'ie >= 8', 'iOS >= 8', 'Android >= 4'],
    }),
]
// 在base.module.rules裏增長
{
    test: /\.module\.less$/,
    loader: ExtractTextPlugin.extract({
        fallback: 'style-loader',  // 將下面處理過的文件插入html中
        use: [
            {
                loader: 'css-loader', 
                // 開啓對css-module的支持,並定義className的輸出格式
                options: { modules: true, importLoaders: 1, localIdentName: '[name]__[local]___[hash:base64:5]'} 
            },
            {
                loader: 'postcss-loader',
                options: {
                    plugins: postcssPlugins
                }
            },
            {
                loader: 'less-loader', 
                // 覆蓋默認的全局配置
                options: {"modifyVars": theme}
            }
        ]
    })
},
// 在 base.plugins 裏增長
new ExtractTextPlugin({
    filename: 'css/[name]-[hash].css',
}),

其餘的 loader 和 webpack plugin 就再也不贅述, 移步文檔

還要注意的是 less-loader 中的配置選項,{"modifyVars": theme} ,這能夠覆蓋 less 文件的配置,能夠用來自定義樣式庫 antd繼續查看

在項目根目錄下新建 theme.js

module.exports = function(){
    return {}
}

別忘記安裝配置文件裏面用到的 loader、pulgins… = =
安裝 less-loader 記得把 less 也裝上,它的文檔也是有強調的哦!

小結

基礎的環境配置就到這裏。生產環境你能夠自行配置,以後我會在後面的文章中列出來。
你能夠在這個倉庫查看 這第一次 commit 哦!下面皆能夠愉快的作本身的博客了!

相關文章
相關標籤/搜索