Django RESTful 系列教程(四)

前后端分離的好處就是可以使前端和后端的開發(fā)分離開來,如果使用 Django 的模板系統(tǒng),我們需要在前端和后端的開發(fā)中不停的切換,前后端分離可以把前端項目和后端項目分離開來,各自建立項目單獨開發(fā)。那么問題來了,前端怎么建項目?這就是本章需要解決的問題。

對于任何的工具,我的哲學是“工具為人服務(wù),而不是人為工具服務(wù)”,希望大家不要為了學習某個工具而學習,任何工具的出現(xiàn)都是為了滿足不同的需求。這是在學習前端工具鏈時需要牢記的一點,不然等學完了,學的東西就全部忘了。前端的世界浩瀚無比,小小的一章并不能很詳盡的介紹它們,僅僅是作為一個入門的介紹。

JavaScript 的解釋器 —— node 與 “模塊化”

js 和 python 同為腳本語言,他們都有自己的解釋器。js 的解釋器是 node 。

在 node 出現(xiàn)前 js 有沒有自己的解釋器呢?有的,那就是我們的瀏覽器中的 js 引擎,但是這個引擎的實現(xiàn)僅僅是針對瀏覽器環(huán)境的,這個引擎限制了 js 的很多功能,比如 js 在瀏覽器引擎下都不能進行文件的讀寫,當然這么做是為了用戶的安全著想。如果我們想要用 js 實現(xiàn) python 的許多功能呢?這時就需要 node 了。

先去這里下載 node ,像安裝 python 一樣把 node 安裝到你的電腦上,記得把安裝路徑添加到環(huán)境變量中。這些都是和安裝 python 是一樣的。

python 運行 .py 腳本是 python <filename>.py 命令,node 也是同理, node <filename>.js 就可以運行一個 js 腳本了。

在上一章,我們在寫 index.js 時需要考慮代碼編寫的順序,這是一件煩人的事情。等到以后代碼量大起來,誰知道哪個組件引用了哪個組件,還容易出現(xiàn) undefined 錯誤。要是我們能單獨把組件都寫在一個地方,要用他們的時候再按照需求引入就好了。也就是,我們希望能夠進行“模塊化”開發(fā)。不用去考慮代碼順序,做到代碼解耦。

js 被創(chuàng)建的時候并沒有考慮到模塊化開發(fā),因為當時的需求還是很簡單的,隨著需求變多,模塊化開發(fā)成了必須。我們知道,我們可以在 python 中使用 import 來引入我們需要的包和庫。 由于在 es6 之前還沒有官方提供這個功能,于是 js 社區(qū)就自己實現(xiàn)了這項需求。這就是 requiremodule.exports 的故事,也就是 CommonJS 規(guī)范。

在 python 中,我們直接使用 import 就可以從一個包中直接導入我們需要的東西。但是 js 有些不同,js 需要被導入的包主動導出內(nèi)部變量,然后其它的包才能導入他們。

在 CommonJS 規(guī)范中,每一個模塊都默認含有一個全局變量 module ,它有一個 exports 屬性,我們可以通過這個屬性來向外暴露內(nèi)部的變量。module.exports 的默認值為一個空對象。外部可以通過全局的 require 函數(shù)來導入其它包內(nèi)的 module.exports 變量。

// A.js
function out(){
    console.log('model A.')
} 
 
module.exports = out // 導出 out 函數(shù)

// B.js
const out = require('./A') // 從 A.js 引入 out
out()

在終端里輸入 node B.js ,你就會看到控制臺打印出了 model A.

就這么簡單。和 Python 的差別就是 js 需要你主動導出變量。這也是 node 引用模塊的方法。

如果你不想寫 module.exports ,還有另外一個全局變量 exports 供你使用,它是 module.exports引用,由于 module.exports 的默認值為一個空對象,所以它的默認值也是一個空對象。如:

// A.js
exports.a = 'a';
exports.b = function(){
    console.log('b')
}

// B.js
const A = require('./A')
console.log(A.a) // 'a'
A.b() // 'b'

有時候我們的模塊不止一個文件,而是有很多個文件。我們可以直接使用 require 來引入模塊路徑,require 會自動搜尋引入目錄下的 index.js 文件,它會把這個文件作為整個模塊的入口。如:

// 模塊 ucag
ucag/
    index.js // module.exports = {
        name: require('./name').name,
        age: require('./age').age,
        job: require('./job').job
    }

    age.js // exports.age = 18
    name.js // exports.name = 'ucag'
    job.js // exports.job = 'student'

我們在一個文件里引入:

const ucag = require('./ucag')
ucag.name // 'ucag'
ucag.age // 18
ucag.job // 'student'

在 es6 之后,js 有了自己引用模塊的方法,它有了自己的 importexport 關(guān)鍵字。對外導出用 export ,對內(nèi)引入用 import。

對于導出,需要遵循以下語法:

export expression
    // 如:
        export var a = 1, b = 2;  // 導出 a 和 b 兩個變量

export {var1, var2, ...} //var1 var2 為導要出的變量

export { v1 as var1, v2 as var2} // 使用 as 來改變導出變量的名字

不過需要注意的是,當我們只想導出一個變量時,我們不能這么寫:

let a = 1;
export a; // 這是錯誤的寫法
export { a } // 這才是正確的寫法

我們可以這樣來引入:

import { var1 }from 'model' // 從 model 導出 var1 變量
import {v1, v2 } from 'model' // 從 model 導出多個變量
import { var1 as v1 }from 'model'  // 從 model 導出 var1 變量并命名為 v1
import * as NewVar from 'model' // 從 model 導入全部的變量

在使用 import 時,import 的變量名要和 export 的變量名完全相同,但是有時候我們我們并不知道一個文件導出的變量叫什么名字,只知道我們需要使用這個模塊默認導出的東西,于是便出現(xiàn)了 default 關(guān)鍵字的使用。我們可以在 export 時使用這個關(guān)鍵字來做到“匿名”導出,在 import 時,隨便取個變量名就可以了。

export default expression
    // 如:
    export default class {} // 導出一個類
    export default {} //導出一個對象
    export default function(){} //導出一個函數(shù)

我們可以這樣來引入:

import NewVar from 'model' // NewVar 是我們?yōu)?export default 導出變量取的名字。

注意,默認導出和命名導出各自的導入是有區(qū)別的:

// 默認導出
export default {
    name:'ucag'
}
// 默認導出對應(yīng)導入
import AnyVarName from 'model' // 沒有花括號
AnyVarName.name // 'ucag'

//命名導出
export var name='ucag'
//命名導出對應(yīng)導入
import { name } from 'model' // 有花括號
name // 'ucag'

//兩種導出方式同時使用
export default {
    name:'ucag'
}
export var age=18;

//兩種導入
import NameObj from 'model' //導入默認導出
import { age } from 'model' //導入命名導出

NameObj.name // 'ucag'
age // 18

總結(jié)一下:

  1. 目前我們學了兩種模塊化的方式。他們是 CommonJS 的模塊化方式與 es6 的模塊化方式。兩種方式不要混用了哦。
  2. CommonJS 規(guī)范:
    1. 使用 module.exportsexports 來導出內(nèi)部變量
    2. 使用 require 導入變量。當被導入對象是路徑時,require 會自動搜尋并引入目錄下的 index.js 文件,會把這個文件作為整個文件的入口。
  3. es6 規(guī)范:
    1. 使用 importexport 來導出內(nèi)部變量
    2. 當導入命名導出變量時,使用基于 import { varName } from 'model' 的語法;當導入匿名或默認導入時,使用 import varName from 'model' 語法;

悲催的是,node 只支持 CommonJS 方式來進行模塊化編寫代碼。

前端的 pip —— npm

剛才我們講了模塊化,現(xiàn)在我們就可以用不同的模塊做很多事情了。 我們可以使用 pip 來安裝 python 的相關(guān)包,在 node 下,我們可以使用 npm 來安裝我們需要的庫。當然,安裝包的工具不止有 npm 一種,還有許多其它的包管理工具供我們使用?,F(xiàn)在的 python 已經(jīng)在安裝時默認安裝了 pip ,node 在安裝時已經(jīng)默認安裝了 npm ,所以我們就用這個現(xiàn)成的工具。

前端項目有個特點 —— 版本更替特別快。今天頁面是一個樣子,明天可能就換成另外的樣子了,變化特別頻繁,可能今天的依賴庫是一個較低的版本,明天它就更新了。所以需要把依賴的庫和項目放在一起,而不是全局安裝到 node 環(huán)境中。每開發(fā)一個新項目就需要重新安裝一次依賴庫。而真正的 node 環(huán)境下可能是什么都沒有的,就一個 npm 。

在一個前端項目中,總是會把依賴庫放進一個文件夾里,然后從這個文件夾里導入需要的庫和依賴,這個文件夾叫做 node_modules 。

在 pip 中,我們可以使用 requirements.txt 來記錄我們的項目依賴。在 npm 下,我們使用 package.json 來記錄依賴。當我們在 package.json 中寫好需要的依賴后,在同一路徑下運行 npm install, npm 會自動搜尋當前目錄下的 package.json 并且自動安裝其中的依賴到 node_modules 中,要是當前目錄沒有 node_modules 目錄,npm 就會幫我們自己創(chuàng)建一個。當我們想要使用別人的項目時,直接把他們的 package.json 拷貝過來,再 npm install 就可以完成開發(fā)環(huán)境的搭建了。這樣是不是特別的方便。

當你在運行完了 npm install 時,如果在以后的開發(fā)中想要再安裝新的包,直接使用 npm install <your-package> 安裝新的包就行了,npm 會自動幫你把新的包裝到當前的 node_modules 下。

在我們發(fā)布一個 python 項目時,我們對于依賴的說明通常是自己寫一個 requirements.txt ,讓用戶們自己去裝依賴。 npm 為我們提供了更加炫酷的功能。在開發(fā)項目時,你直接在含有 package.json 的目錄下運行 npm install <your-package> --save-dev ,npm 會自動幫你把依賴寫到 package.json 中。以后你就可以直接發(fā)布自己的項目,都不用在 package.json 中手寫依賴。

通過上面的內(nèi)容我們知道,我們只需要在一個文件夾中創(chuàng)建好 package.json ,就可以自動安裝我們的包了。 我們還可以使用 npm 自動生成這個文件。在一個空目錄下,運行 npm init ,npm 會問你一些有的沒的問題,你可以隨便回答,也可以一路回車什么都不答,目錄下就會自動多一個 package.json 文件。比如我們在一個叫做 vue-test 的路徑下運行這個命令,記得以管理員權(quán)限運行。

λ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (vue-test)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to C:\Users\Administrator\Desktop\vue-test\package.json:

{
  "name": "vue-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

如果你不想按回車,在運行 npm init 時加一個 -y 參數(shù),npm 就會默認你使用它生成的答案。也就是運行 npm init -y 就行了。

λ npm init -y
Wrote to C:\Users\Administrator\Desktop\vue-test\package.json:

{
  "name": "vue-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

然后在以后的安裝中,我們使用 npm install --save-dev ,就會自動把依賴庫安裝到 node_modules 中,把相關(guān)庫依賴的版本信息寫入到 package.json 中。

還是以剛才的 vue-test 為例,在創(chuàng)建完了 package.json 后,運行:

λ npm install --save-dev jquery
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN vue-test@1.0.0 No description
npm WARN vue-test@1.0.0 No repository field.

+ jquery@3.2.1
added 1 package in 5.114s

此時,我們發(fā)現(xiàn)又多了一個 package-lock.json文件, 先不管它。我們再打開 package.json 看看,你會發(fā)現(xiàn)它的內(nèi)容變成了這樣:

λ cat package.json
{
  "name": "vue-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jquery": "^3.2.1"
  }
}

依賴已經(jīng)自動寫入了 package.json 中。我們再刪除 node_modules 文件夾和 package-lock.json ,只留下 package.json 。再運行 npm install。

λ npm install
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN vue-test@1.0.0 No description
npm WARN vue-test@1.0.0 No repository field.

added 1 package in 5.4s

我們發(fā)現(xiàn) npm 已經(jīng)為我們安裝好了依賴。

當然,我們有時需要一些各個項目都會用到的工具。還是以 python 為例,我們會使用 virtualenv 來創(chuàng)建虛擬環(huán)境,在安裝它時,我們直接全局安裝到了系統(tǒng)中。npm 也可以全局安裝我們的包。在任意路徑下,使用 npm install -g <your-package> 就可以全局安裝一個包了。我們在以后會用到一個工具叫做 vue-cli ,我們可以用它來快速的創(chuàng)建一個 vue 項目。為什么要用它呢,在前端項目中,有一些庫是必須要用到的比如我們的 webpack ,比如開發(fā) vue 需要用到的 vue 包,vue-router,vuex 等等,它會幫我們把這些自動寫入 package.json 中,并且會為我們建立起最基本的項目結(jié)構(gòu)。就像是我們使用 django-admin 來創(chuàng)建一個 Django 項目一樣。這樣的工具,在前端被稱為“腳手架”

任意路徑下運行:

npm install -g vue-cli

vue 腳手架就被安裝到了我們的 node 環(huán)境中。我們就可以在命令行中使用 vue 命令來創(chuàng)建新的項目了,不需要自己手動創(chuàng)建項目。大家可以試著運行 vue --help ,看看你是否安裝成功了 vue-cli

λ vue --help

  Usage: vue <command> [options]


  Options:

    -V, --version  output the version number
    -h, --help     output usage information


  Commands:

    init        generate a new project from a template
    list        list available official templates
    build       prototype a new project
    help [cmd]  display help for [cmd]

npm 除了可以安裝包之外,還可以使用 npm run 用來管理腳本命令。
還是以剛才安裝 'jquery' 的包為例,打開 package.json ,把 scripts 字段改成這樣:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "vh": "vue --help"
  }

然后在 package.json 路徑下運行 npm run vh ,你就會看到控制臺輸出了 vue 腳手架的幫助信息。

當我們運行 npm run <cmd>時,npm 會搜尋同目錄下的 package.json 中的 scripts 中對應(yīng)的屬性,然后把當前的 node_modules 加入環(huán)境變量中,執(zhí)行其中命令,這樣就不用我們每次都都手動輸入長長的命令了。

還是總結(jié)一下:

  1. npm 是 node 中的包管理工具。
  2. npm install: 安裝 package.json 中的依賴到 node_modules 中。
  3. npm install <package-name> --save-dev:把包安裝到 node_modules 中,并把包依賴寫入 package.json 中。
  4. npm install <package-name> -g:全局安裝某個包。
  5. npm run <cmd>: 運行當前目錄下 package.jsonscripts 中的命令。

前端工具鏈

前端開發(fā)會用到許許多多的工具,有的工具是為了更加方便的開發(fā)而生,有的工具是為了使代碼更好的適應(yīng)瀏覽器環(huán)境。每個工具的出現(xiàn)都是為了解決特定的問題。

解決版本差異 —— babel

版本差異一直是個很讓人頭痛的問題。用 python2 寫的代碼,大概率會在 python3 上運行失敗。 js 是運行在瀏覽器上的,很多的瀏覽器更新并沒有能夠很穩(wěn)定的跟上 js 更新的步伐,有的瀏覽器只支持到 es5 ,或者只支持部分 es6 特性。為了能夠向下兼容,大家想了辦法 —— 把 es6 的代碼轉(zhuǎn)換為 es5 的代碼就行了!開發(fā)的時候用 es6 ,最后再把代碼轉(zhuǎn)換成 es5 代碼就行了!于是便出現(xiàn)了 babel 。

創(chuàng)建一個叫做 babel-test 的文件夾,在此路徑下運行:

npm init -y
npm install --save-dev babel-cli

在使用 babel 前,我們需要通過配置文件告訴它轉(zhuǎn)碼規(guī)則是什么。babel 默認的配置文件名為 .babelrc
babel-test 下創(chuàng)建 .babelrc,寫入:

  {
    "presets": [
      "es2015"
    ],
    "plugins": []
  }

轉(zhuǎn)碼規(guī)則是以附加規(guī)則包的形式出現(xiàn)的。所以在配置好了之后我們還需要安裝規(guī)則包。

npm install --save-dev babel-preset-es2015

babel 是以命令行的形式使用的,最常用的幾個命令如下:

# 轉(zhuǎn)碼結(jié)果打印到控制臺
 babel es6.js

# 轉(zhuǎn)碼結(jié)果寫入一個文件
 babel es6.js -o es5.js # 將 es6.js 的轉(zhuǎn)碼結(jié)果寫入 es5.js 中

# 轉(zhuǎn)碼整個目錄到指定路徑
 babel es6js -d es5js # 將 es6js 路徑下的 js 文件轉(zhuǎn)碼到 es5js 路徑下

但是由于我們的 babel 是安裝在 babel-test 的 node_modules 中的,所以需要使用 npm run 來方便運行以上命令。

編輯 package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "babel inputs -d outputs"
  }

在 babel-test 下創(chuàng)建一個新的目錄 inputs ,在其中寫入新的文件 a.js:

// es6 語法,模板字符串
let name = 'ucag'
let greeting = `hello my name is ${name}`

然后運行:

npm run build

在轉(zhuǎn)換完成之后,我們在 outputs 下找到 a.js,發(fā)現(xiàn)代碼變成了這樣:

'use strict';

var name = 'ucag';
var greeting = 'hello my name is ' + name;

es6 代碼已經(jīng)被轉(zhuǎn)換為了 es5 代碼。

整合資源 —— webpack

在一個前端項目中,會有許許多多的文件。更重要的是,最后我們需要通過瀏覽器來運行他們。我們用 es6 寫的代碼,需要轉(zhuǎn)換一次之后才能上線使用。如果我們用的是 TypeScript 寫的 js ,那我們還需要先把 TypeScript 轉(zhuǎn)換為原生 js ,再用 babel 轉(zhuǎn)換為 es5 代碼。如果我們使用的是模塊化開發(fā),但是瀏覽器又不支持,我們還需要把模塊化的代碼整合為瀏覽器可執(zhí)行的代碼??傊瑸榱朔奖汩_發(fā)與方便在瀏覽器上運行,我們需要用到許許多多的工具。

webpack 最重要的功能就是可以把相互依賴的模塊打包。我們在模塊化開發(fā)之后,可能會產(chǎn)生許多的 js 文件,要是一個個手寫把他們引入到 html 中是一件很麻煩的事情,所以我們此時就需要 webpack 來幫我們把分離的模塊組織到一起,這樣就會方便很多了
創(chuàng)建一個新的路徑 webpack-test ,在此路徑下運行:

npm init -y
npm install --save-dev webpack

使用前先配置,在配置之前我們需要知道一些最基本的概念。

  1. 入口 entry:不管一個項目有多復雜,它總是有一個入口的。這個入口就被稱為 entry 。這就像是我們的模塊有個 index.js 一樣。
  2. 出口 output:webpack 根據(jù)入口文件將被依賴的文件按照一定的規(guī)則打包在一起,最終需要一個輸出打包文件的地方,這就是 output 。

這就是最基本的概念了,我們會在以后的教程中學習到更多有關(guān) webpack 配置的知識,不過由于我們目前的需求還很簡單,還用不到其它的一些功能,就算是現(xiàn)在講了也難以體會其中的作用,所以我們目前不著急,慢慢來。

webpack 有多種加載配置的方法,一種是寫一個獨立的配置文件,一種是在命令行內(nèi)編寫配置,還有許多其它更靈活編寫配置的方法,我們以后再說。當我們在 webpack-test 下不帶任何參數(shù)運行 webpack 命令時,webpack 會自動去尋找名為 webpack.config.js 的文件,這就是它默認的配置文件名了。

在 webpack-test 下創(chuàng)建一個新的文件 webpack.config.js:

module.exports = {
    entry:'./main.js', // 入口文件為當前路徑下的 main.js 為文件
    output:{
        path:__dirname, // __dirname 是 node 中的全局變量,代表當前路徑。
        filename:'index.js' // 打包之后的文件名
    }
}

編輯 package.json

"scripts"{
    'pkg':'webpack' // 編輯快捷命令
}

以第三章的 index.js 為例,當時我們把所有的代碼都寫到了一個文件中,現(xiàn)在我們可以把他們分開寫了,最后再打包起來。

創(chuàng)建幾個新文件分別為 components.js api.js store.js main.js vue.js jquery.js

vue.js: vue 源文件
jquery: jquery 源文件

api.js:

let api = {
    v1: {
        run: function () {
            return '/api/v1/run/'
        },
        code: {
            list: function () {
                return '/api/v1/code/'
            },
            create: function (run = false) {
                let base = '/api/v1/code/';
                return run ? base + '?run' : base
            },
            detail: function (id, run = false) {
                let base = `/api/v1/code/${id}/`;
                return run ? base + '?run' : base
            },
            remove: function (id) {
                return api.v1.code.detail(id, false)
            },
            update: function (id, run = false) {
                return api.v1.code.detail(id, run)
            }
        }
    }
}

module.exports = api // 導出 API 

store.js

const $ = require('./jquery') // 引入 jquery 

let store = {
    state: {
        list: [],
        code: '',
        name: '',
        id: '',
        output: ''
    },
    actions: {
        run: function (code) { //運行代碼
            $.post({
                url: api.v1.run(),
                data: {code: code},
                dataType: 'json',
                success: function (data) {
                    store.state.output = data.output
                }
            })
        },
        runDetail: function (id) { //運行特定的代碼
            $.getJSON({
                url: api.v1.run() + `?id=${id}`,
                success: function (data) {
                    store.state.output = data.output
                }
            })
        },
        freshList: function () { //獲得代碼列表
            $.getJSON({
                url: api.v1.code.list(),
                success: function (data) {
                    store.state.list = data
                }
            })
        },
        getDetail: function (id) {//獲得特定的代碼實例
            $.getJSON({
                url: api.v1.code.detail(id),
                success: function (data) {
                    store.state.id = data.id;
                    store.state.name = data.name;
                    store.state.code = data.code;
                    store.state.output = '';
                }
            })
        },
        create: function (run = false) { //創(chuàng)建新代碼
            $.post({
                url: api.v1.code.create(run),
                data: {
                    name: store.state.name,
                    code: store.state.code
                },
                dataType: 'json',
                success: function (data) {
                    if (run) {
                        store.state.output = data.output
                    }
                    store.actions.freshList()
                }
            })
        },
        update: function (id, run = false) { //更新代碼
            $.ajax({
                url: api.v1.code.update(id, run),
                type: 'PUT',
                data: {
                    code: store.state.code,
                    name: store.state.name
                },
                dataType: 'json',
                success: function (data) {
                    if (run) {
                        store.state.output = data.output
                    }
                    store.actions.freshList()
                }
            })
        },
        remove: function (id) { //刪除代碼
            $.ajax({
                url: api.v1.code.remove(id),
                type: 'DELETE',
                dataType: 'json',
                success: function (data) {
                    store.actions.freshList()
                }
            })
        }
    }
}

store.actions.freshList() // Store的初始化工作,先獲取代碼列表

module.exports = store // 導出 store

components.js

const store = require('./store')
let list = { //代碼列表組件
    template: `
    <table class="table table-striped">
        <thead> 
            <tr>
                <th class="text-center">文件名</th> 
                <th class="text-center">選項</th> 
            </tr>
        </thead>
        <tbody>
            <tr v-for="item in state.list">
            <td class="text-center">{{ item.name }}</td>
            <td>
                <button class='btn btn-primary' @click="getDetail(item.id)">查看</button>
                <button class="btn btn-primary" @click="run(item.id)">運行</button>
                <button class="btn btn-danger" @click="remove(item.id)">刪除</button>
            </td>
            </tr>
        </tbody> 
    </table>
    `,
    data() {
        return {
            state: store.state
        }
    },
    methods: {
        getDetail(id) {
            store.actions.getDetail(id)
        },
        run(id) {
            store.actions.runDetail(id)
        },
        remove(id) {
            store.actions.remove(id)
        }
    }
}
let options = {//代碼選項組件
    template: `
    <div style="display: flex;
         justify-content: space-around;
         flex-wrap: wrap" >
        <button class="btn btn-primary" @click="run(state.code)">運行</button>
        <button class="btn btn-primary" @click="update(state.id)">保存</button>
        <button class="btn" @click="update(state.id, true)">保存并運行</button>
        <button class="btn btn-primary" @click="newOptions">New</button>
    </div>
    `,
    data() {
        return {
            state: store.state
        }
    },
    methods: {
        run(code) {
            store.actions.run(code)
        },
        update(id, run = false) {
            if (typeof id == 'string') {
                store.actions.create(run)
            } else {
                store.actions.update(id, run)
            }
        },
        newOptions() {
            this.state.name = '';
            this.state.code = '';
            this.state.id = '';
            this.state.output = '';
        }
    }
}
let output = { //代碼輸出組件
    template: `
    <textarea disabled 
    class="form-control text-center">{{ state.output }}</textarea>
    `,
    data() {
        return {
            state: store.state
        }
    },
    updated() {
        let ele = $(this.$el);
        ele.css({
            'height': 'auto',
            'overflow-y': 'hidden'
        }).height(ele.prop('scrollHeight'))
    }
}
let input = { //代碼輸入組件
    template: `
    <div class="form-group">
        <textarea 
        class="form-control" 
        id="input"
        :value="state.code"
        @input="inputHandler"></textarea> 
        <label for="code-name-input">代碼片段名</label>
        <p class="text-info">如需保存代碼,建議輸入代碼片段名</p>
        <input 
        type="text" 
        class="form-control" 
        :value="state.name"
        @input="(e)=> state.name = e.target.value">
    </div>
    `,
    data() {
        return {
            state: store.state
        }
    },
    methods: {
        flexSize(selector) {
            let ele = $(selector);
            ele.css({
                'height': 'auto',
                'overflow-y': 'hidden'
            }).height(ele.prop('scrollHeight'))
        },
        inputHandler(e) {
            this.state.code = e.target.value;
            this.flexSize(e.target)
        }
    }
}

module.exports = {
    list, input, output, options
} // 導出組件

main.js

const cmp = require('./components') //引入組件
const list = cmp.list
const options = cmp.options
const input = cmp.input
const output = cmp.output
const Vue = require('./vue')

let app = { //整體頁面布局
    template: `
        <div class="continer-fluid">
            <div class="row text-center h1">
                在線 Python 解釋器
            </div>
            <hr>
            <div class="row">
                <div class="col-md-3">
                    <code-list></code-list>
                </div>
                <div class="col-md-9">
                    <div class="container-fluid">
                        <div class="col-md-6">
                            <p class="text-center h3">請在下方輸入代碼:</p>
                            <code-input></code-input>
                            <hr>
                            <code-options></code-options>
                        </div>
                        <p class="text-center h3">輸出</p>
                        <div class="col-md-6">
                            <code-output></code-output>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    `,
    components: {
        'code-input': input,
        'code-list': list,
        'code-options': options,
        'code-output': output
    }
}

let root = new Vue({ //根組件,整個頁面入口
    el: '#app',
    template: '<app></app>',
    components: {
        'app': app
    }
})

在 webpack-test 下運行:

npm run pkg # 運行 webpack

過一會兒你就會發(fā)現(xiàn)多了一個 index.js 文件,這就是我們打包的最終結(jié)果了。 6 個 js 文件被打包成了一個文件,最終打包的 index.js 的功能和那 6 個 js 文件的功能都是一樣的。并且瀏覽器可以正常執(zhí)行這些代碼, webpack 已經(jīng)為我們整合好代碼,瀏覽器中不會出現(xiàn)模塊化支持的問題。

要是你把 index.js 放到我們的 index.html 里,先不引入 bootstrap ,你會發(fā)現(xiàn)頁面還是可以正常使用的。功能和前面完全相同!如果我們不使用 webpack ,那么我們需要在 html 頁面按照順序挨著寫 <script src=""></script> 。


本章前端工具鏈的部分就簡單的介紹到這里。我們并沒有打包 bootstrap,要是你把 bootstrap.js 和我們的代碼打包到一起,你會發(fā)現(xiàn)它在控制臺報錯了。這是 webpack 的問題嗎?這是我們之后要解決的問題。保持你的好奇心。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容