由webpack引發的前端自動化講解

advence-liz發表於2019-04-11

紀念第一次講課,紀念 avepont ,紀念長春,紀念倆年前青澀的自己,紀念windows,本文是兩年前第一次講課所準備的課件。

對NODE的誤解

  • NODE 肯定是幾個前端工程師在實驗室裡搗鼓出來的。
  • 為了後端而後端,有意思嗎?
  • 怎麼又發明了一門新語言?
  • javascript 承擔的責任太重了
  • 直覺上,JavaScript不應該執行在後端
  • 前端工程師要逆襲了

圖靈完備語言

一切可計算的問題都能計算,這樣的虛擬機器或者程式語言就叫圖靈完備的。

一個能計算出每個圖靈可計算函式(Turing-computable function)的計算系統被稱為圖靈完備的。一個語言是圖靈完備的,意味著該語言的計算能力與一個通用圖靈機 (Universal Turing Machine)相當,這也是現代計算機語言所能擁有的最高能力。

現在的計算機程式語言都是圖靈完全(完備)的。

因此,這世上也不存在一種語言可以做,而另一種語言不可以做的事兒

NODE

Ryan Dahl 是一名資深的程式設計師,在創造出NODE之前,他的主要工作都是圍繞高效能Web伺服器進行的。經歷過一些嘗試和失敗之後,他找到了設計高效能Web伺服器的幾個要點:事件驅動,非阻塞I/O.

經過C,Lua,Haskell,Ruby,javascript等權衡最終選擇的了javascript。

起初,Ryan Dahl稱她的專案為web.js,就是一個Web伺服器,但是專案的發展超過了他最初構想,變成了一個構建網路應用的基礎框架。每個NODE程式都構成網路應用中的一個節點,這就是NODE名字所含意義的真諦。

雖然NODE這麼酷炫但是我們都不用我們只用它寫指令碼。

Chrome&Node

由webpack引發的前端自動化講解

NODE&Browser&W3C&ECMASCRIPT

由webpack引發的前端自動化講解

NPM

npm 即node的安裝包管理工具(就像nuget之於.NET,pip之於python)

npm 命令手冊

  • npm -v 顯示版本資訊
  • npm install [--save]?
$ npm install sax@latest
$ npm install sax@0.1.1
$ npm install sax@">=0.1.0 <0.2.0"
複製程式碼
  • npm update
  • npm -l //檢視每個命令的用法
  • npm info npm
  • npm run

npm install eslint uglify-js --save

"scripts": {
  "lint": "eslint script.js ",
  "compile": "babel script.js",
  "uglifyjs ":"uglifyjs inet.js -o inet-min.js",
  "template": "node bin/templete.js",
  "launch": "launch.cmd",
  "build":"npm run lint &&npm run compile&&npm run launch",
  ""
},

複製程式碼

指令&資料

這兩個概念在計算機世界無處不在,一般資料的載體的就是檔案,而這個檔案在一定的環境下又變成了指令。如:一個HTML檔案放在伺服器上就是資料,而當瀏覽器獲取了它,並將其解析絢麗的頁面它就成了指令。

而前端主要由三種資料組成,HTML,CSS,JS,因此前端自動化就是用腳步自動化處理前端所涉及的資料(檔案)。

而這個腳步呢就用NODE寫,其一前端開發對JS技術栽比較熟悉容易上手,其二NODE社群灰常活躍你基本能找到你想要的所有東西。

不過大家在網上檢索前端自動化,基本都會感覺前端自動化是grunt,gulp,webpack...,或者因為NODE才有了前端自動化。

其實一直都存在,只是之前更多的是java,python...的實現,就像現在找尋一些工具基本也都是java,python,ruby 版的。

前端自動化都做什麼

  • 壓縮CSS,js。
  • 預編譯HTML,JS,CSS 前端涉及到的語言。HTML ,CSS 抽象程度比較低為了更高效的開發一般 HTML,css 由 jade,less 等DSL(Domain Specific Language)編譯而成。
  • 語法檢查,格式整理,自動重新整理頁面等其它功能。 當前主流的所有工具,基本都會提供兩種呼叫方式: CLI & NODEAPI

命令列程式

PATH

PATH=
C:\Windows\system32;
C:\Windows;C:\Windows\System32\Wbem;
C:\Windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files (x86)\nodejs\;
C:\Program Files\Git\cmd;
C:\Program Files\dotnet\;
C:\Program Files\TortoiseGit\bin;C:\Users\Zhuo.Li\AppData\Local\Programs\Python\Python35\Scripts\;
C:\Users\Zhuo.Li\AppData\Local\Programs\Python\Python35\;
C:\Users\Zhuo.Li\AppData\Roaming\npm;
複製程式碼

PATHEXT

C:\Users\Zhuo.Li>echo %PATHEXT%
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
複製程式碼

Hello World

echo  Hello World
複製程式碼

node 全域性命令呼叫方式

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\node_modules\gulp\bin\gulp.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\node_modules\gulp\bin\gulp.js" %*
)
複製程式碼

模組組織

隨著javascript發展,從增強顯示的指令碼到解決一類問題的庫,然後構建應用,一個有效的模組載入方案也就成為了必須的元素。 因為當想用一個語言構建一個大型應用,模組機制不可或缺。

  • 瀏覽器端運用最廣泛的為 AMD 規範
  • 服務端使用 CommonJS 規範
  • 而ES6 Module 載入規範不遠的將來將要統一前後端(我們要是採用ES6 載入規範)

-javascript模組化程式設計

require AMD 寫在回撥中是因為如果同步等他瀏覽器可能會卡死.

關於javascriptIDE 目前功能薄弱的思考

javascript 尤其執行在瀏覽器端並沒標準統一的入口,通過簡陋的<script>標籤引入,所以無法判斷一個檔案中出現的物件該有何種行為,而且script 還可能是動態載入的。 必然不能像其他語言那樣智慧檢驗差錯與提示,如果以後模組化程式設計根深蒂固,javascriptIDE也會像其他語言一樣強大。 有必要的話興許還能實時預覽,因為現在整合webkit渲染引擎開發桌面的應用正在蓬勃發展(比如我正在使用的vscode)

AMD

define(['module1', 'module2'], function(m1, m2) {

    return {
        method: function() {
            m1.methodA();
			m2.methodB();
        }
    };

});

require(['foo', 'bar'], function ( foo, bar ) {
        foo.doSomething();
});
複製程式碼

cmomonjs

//index.js
const m1=require("module1");

m1.dosomething()
.........

//module1
......

module.exports={
dosomething:function(){
    ....
}
}

複製程式碼

ES6

import

//import
import { stat as _stat, exists, readFile } from 'fs';
//由於import是靜態執行,所以不能使用表示式和變數,這些只有在執行時才能得到結果的語法結構。

// 報錯
import { 'f' + 'oo' } from 'my_module';

// 報錯
let module = 'my_module';
import { foo } from module;

// 報錯
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}
//上面三種寫法都會報錯,因為它們用到了表示式、變數和if結構。在靜態分析階段,這些語法都是沒法得到值的。
複製程式碼

export

export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//上面程式碼是profile.js檔案,儲存了使用者資訊。ES6 將其視為一個模組,裡面用export命令對外部輸出了三個變數。

//export的寫法,除了像上面這樣,還有另外一種。

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName as _firstName, lastName, year};


複製程式碼

export default

/ export-default.js
export default function () {
  console.log('foo');
}
上面程式碼是一個模組檔案export-default.js,它的預設輸出是一個函式。

其他模組載入該模組時,import命令可以為該匿名函式指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'
複製程式碼

aui.js

"use strict";

var aui ={
    version:'1.0.0'
}
function func(){

    return aui.version;
}
//export default aui;
export default func;
複製程式碼

widget.js

var combobox = {
    name:'combobox'
}

var dialog = {
    name:'dialog'
}

export { combobox,dialog}
複製程式碼

index.js

import aui from './aui';

import {combobox,dialog as _dialog} from './widget';

console.dir(combobox.name);

console.log(_dialog.name);

console.dir(func());
複製程式碼

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="./lib/library1"></script>
    <script src="./lib/library2"></script>
    <script src="global.common.js"></script>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
   
    <script type="text/javascript" src="index.js"></script>
  </body>   
</html>
複製程式碼

webpack

webpack 是一個現代的 JavaScript 應用程式的模組打包器(module bundler)。當 webpack 處理應用程式時,它會遞迴地構建一個依賴關係圖表(dependency graph),其中包含應用程式需要的每個模組,然後將所有這些模組打包成少量的 bundle - 通常只有一個,由瀏覽器載入。

它是高度可配置的,但是,在開始前你需要先理解四個核心概念:入口(entry)、輸出(output)、loader、外掛(plugins)。

使用配置檔案的用法 webpack [--config webpack.config.js]

$ webpack --config example.config.js
複製程式碼

不使用配置檔案的用法 webpack []

$ webpack src/index.js dist/bundle.js
複製程式碼

NODE API

const webpack = require("webpack");

webpack({
  // 配置物件
}, (err, stats) => {
  if (err || stats.hasErrors()) {
    // 在這裡處理錯誤
  }
  // 處理完成
});
複製程式碼

webpackConfig

//webpack.config.js

var pkg = require("./package"),
    options = process.argv,
    config;

/**
 * pkg.pattern {dev|prod} 原計劃package.json 中配置是否為product 目前沒有使用
 */
config = require("./webpack." + pkg.env);
module.exports = config;
複製程式碼
//var webpack = require('webpack');
var path = require("path"),
    HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: {
        bundle: "./src/hello.js",
        // react:"./src/react.js",
        index:"./webpack_demo/index.js"
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: "[name].js"

    },
    module: {
        rules: [
            // {
            //     test: /\.js$/,
            //     enforce: "pre",
            //     loader: "eslint-loader"
            // },
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: [{
                    loader: 'babel-loader'
                }]
            }]

    },
    context: __dirname,
    devtool: "source-map",
    target: "web",
    resolve: {
        // options for resolving module requests
        // (does not apply to resolving to loaders)
        modules: [
            "node_modules",
            path.resolve(__dirname, "node_modules")
        ],
        // directories where to look for modules
        extensions: [".js", ".json", ".jsx", ".css"],
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'template/_layout.html'
        })
    ],
    devServer: {
        contentBase: path.join(__dirname, "build"),
        compress: true,
        port: 9000
    }

};
複製程式碼

gulp

const gulp = require('gulp');
const eslint = require('gulp-eslint');
 
gulp.task('lint', () => {
    // ESLint ignores files with "node_modules" paths. 
    // So, it's best to have gulp ignore the directory as well. 
    // Also, Be sure to return the stream from the task; 
    // Otherwise, the task may end before the stream has finished. 
    return gulp.src(['**/*.js','!node_modules/**'])
        // eslint() attaches the lint output to the "eslint" property 
        // of the file object so it can be used by other modules. 
        .pipe(eslint())
        // eslint.format() outputs the lint results to the console. 
        // Alternatively use eslint.formatEach() (see Docs). 
        .pipe(eslint.format())
        // To have the process exit with an error code (1) on 
        // lint error, return the stream and pipe to failAfterError last. 
        .pipe(eslint.failAfterError());
});
 
gulp.task('default', ['lint'], function () {
    // This will only run if the lint task is successful... 
});
複製程式碼

grunt

require('load-grunt-tasks')(grunt); // npm install --save-dev load-grunt-tasks 
 
grunt.initConfig({
    eslint: {
        target: ['file.js']
    }
});
 
grunt.registerTask('default', ['eslint']);
複製程式碼

由webpack引發的前端自動化講解

DEMO

CLI_DEMO

helloworld

echo  Hello World
複製程式碼

由webpack引發的前端自動化講解

ESlint(主要演示npm命令)

npm init //https://github.com/advence-liz/nodecmd/blob/master/

由webpack引發的前端自動化講解

npm install

由webpack引發的前端自動化講解

node index.js

由webpack引發的前端自動化講解

package.json

{
  "name": "eslint-demo",
  "version": "1.0.0",
  "description": "a demo for eslint",
  "main": "index.js",
  "scripts": {
    "test": "npm run test"
  },
  "author": "liz",
  "license": "ISC",
  "dependencies": {
    "eslint": "^3.19.0"
  }
}
複製程式碼

index.js

"use strict";
var CLIEngine = require("eslint").CLIEngine;

var cli = new CLIEngine({
    envs: ["browser", "mocha"],
    useEslintrc: false,
    rules: {
        semi: 2
    }
});

// lint the supplied text and optionally set
// a filename that is displayed in the report
var report = cli.executeOnText("test.js");
console.dir(report);
複製程式碼

run webpack

由webpack引發的前端自動化講解

run webpack-dev-server

由webpack引發的前端自動化講解

webpack NODEAPI

js

const webpack = require("webpack");
//const pkg = require("../package");
const webpack_config= require("../webpack.dev.js")

webpack(webpack_config, (err, stats) => {
    console.log(stats.toString());
//   if (err || stats.hasErrors()) {
//     // 在這裡處理錯誤
//   }
  // 處理完成
});
複製程式碼

webpack.config

//var webpack = require('webpack');
var path = require("path"),
    HtmlWebpackPlugin = require('html-webpack-plugin');


module.exports = {
    entry: {
        bundle: "./src/hello.js",
        // react:"./src/react.js",
        index:"./webpack_demo/index.js"
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: "[name].js"

    },
    module: {
        rules: [
            // {
            //     test: /\.js$/,
            //     enforce: "pre",
            //     loader: "eslint-loader"
            // },
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: [{
                    loader: 'babel-loader'
                }]
            }]

    },
    context: __dirname,
    devtool: "source-map",
    target: "web",
    resolve: {
        // options for resolving module requests
        // (does not apply to resolving to loaders)
        modules: [
            "node_modules",
            path.resolve(__dirname, "node_modules")
        ],
        // directories where to look for modules
        extensions: [".js", ".json", ".jsx", ".css"],
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'template/_layout.html'
        })
    ],
    devServer: {
        contentBase: path.join(__dirname, "build"),
        compress: true,
        port: 9000
    }

};
複製程式碼

執行展示

由webpack引發的前端自動化講解

HtmlWebpackPlugin

_layout.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="http://cdn.bootcss.com/react/15.4.2/react.js"></script>
    <script src="http://cdn.bootcss.com/react/15.4.2/react-dom.js"></script>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
     <jspath>
  </body>
</html>
複製程式碼

template.js

const fs=require("fs");
const path= require("path");
/**
 * 此處readFile&writeFile 沒有使用相對路徑,因為如果是相對路徑,是相對於當前程式所在的路徑(process.cmd()),而不是相對於當前指令碼所在的路徑。
 */
var fileName= path.resolve(__dirname,'_layout.html');
var distPath = path.resolve(__dirname,'index.html');
var template = fs.readFileSync(fileName, 'utf8');

template = template.toString().replace(/<jspath>/,`<script src="bundle.js"></script>`);

fs.writeFile(distPath,template,(err) => {
  if (err) throw err;
  console.log('It\'s saved!');
});
複製程式碼

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="http://cdn.bootcss.com/react/15.4.2/react.js"></script>
    <script src="http://cdn.bootcss.com/react/15.4.2/react-dom.js"></script>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
     <script src="bundle.js"></script>
  </body>
</html>
複製程式碼

廢稿

javascript

1995年5月,Brendan Eich只用了10天,就設計完成了這種語言的第一版。它是一個大雜燴,語法有多個來源:

基本語法:借鑑C語言和Java語言。 資料結構:借鑑Java語言,包括將值分成原始值和物件兩大類。 函式的用法:借鑑Scheme語言和Awk語言,將函式當作第一等公民,並引入閉包。 原型繼承模型:借鑑Self語言(Smalltalk的一種變種)。 正規表示式:借鑑Perl語言。 字串和陣列處理:借鑑Python語言。 為了保持簡單,這種指令碼語言缺少一些關鍵的功能,比如塊級作用域、模組、子型別(subtyping)等等,但是可以利用現有功能找出解決辦法。這種功能的不足,直接導致了後來JavaScript的一個顯著特點:對於其他語言,你需要學習語言的各種功能,而對於JavaScript,你常常需要學習各種解決問題的模式。而且由於來源多樣,從一開始就註定,JavaScript的程式設計風格是函數語言程式設計和麵向物件程式設計的一種混合體。

first-class

通常,程式語言會限制操作計算元素的途徑。帶有最少限制的元素被稱為具有一等地位。一些一等元素的“權利和特權”是:

  • 它們可以繫結到名稱。
  • 它們可以作為引數向函式傳遞。
  • 它們可以作為函式的返回值返回。
  • 它們可以包含在好素具結構中。

ES6 的模組自動採用嚴格模式,不管你有沒有在模組頭部加上"use strict"

  • 變數必須宣告後再使用
  • 函式的引數不能有同名屬性,否則報錯
  • 不能使用with語句
  • 不能對只讀屬性賦值,否則報錯
  • 不能使用字首0表示八進位制數,否則報錯
  • 不能刪除不可刪除的屬性,否則報錯
  • 不能刪除變數delete prop,會報錯,只能刪除屬性delete global[prop]
  • eval不會在它的外層作用域引入變數
  • eval和arguments不能被重新賦值
  • arguments不會自動反映函式引數的變化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全域性物件
  • 不能使用fn.caller和fn.arguments獲取函式呼叫的堆疊
  • 增加了保留字(比如protected、static和interface)

相關文章