前后端分離的好處就是可以使前端和后端的開發(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)了這項需求。這就是 require 和 module.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 有了自己引用模塊的方法,它有了自己的 import 和 export 關(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é)一下:
- 目前我們學了兩種模塊化的方式。他們是 CommonJS 的模塊化方式與 es6 的模塊化方式。兩種方式不要混用了哦。
- CommonJS 規(guī)范:
- 使用
module.exports或exports來導出內(nèi)部變量 - 使用
require導入變量。當被導入對象是路徑時,require會自動搜尋并引入目錄下的index.js文件,會把這個文件作為整個文件的入口。
- 使用
- es6 規(guī)范:
- 使用
import與export來導出內(nèi)部變量 - 當導入命名導出變量時,使用基于
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é)一下:
- npm 是 node 中的包管理工具。
-
npm install: 安裝package.json中的依賴到node_modules中。 -
npm install <package-name> --save-dev:把包安裝到node_modules中,并把包依賴寫入package.json中。 -
npm install <package-name> -g:全局安裝某個包。 -
npm run <cmd>: 運行當前目錄下package.json的scripts中的命令。
前端工具鏈
前端開發(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
使用前先配置,在配置之前我們需要知道一些最基本的概念。
- 入口 entry:不管一個項目有多復雜,它總是有一個入口的。這個入口就被稱為 entry 。這就像是我們的模塊有個
index.js一樣。 - 出口 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 的問題嗎?這是我們之后要解決的問題。保持你的好奇心。