前后端分離的好處就是可以使前端和后端的開發(fā)分離開來,如果使用 Django 的模板系統(tǒng)县踢,我們需要在前端和后端的開發(fā)中不停的切換,前后端分離可以把前端項(xiàng)目和后端項(xiàng)目分離開來,各自建立項(xiàng)目單獨(dú)開發(fā)蛙粘。那么問題來了垫卤,前端怎么建項(xiàng)目?這就是本章需要解決的問題出牧。
對于任何的工具穴肘,我的哲學(xué)是“工具為人服務(wù),而不是人為工具服務(wù)”舔痕,希望大家不要為了學(xué)習(xí)某個(gè)工具而學(xué)習(xí)评抚,任何工具的出現(xiàn)都是為了滿足不同的需求。這是在學(xué)習(xí)前端工具鏈時(shí)需要牢記的一點(diǎn)伯复,不然等學(xué)完了慨代,學(xué)的東西就全部忘了。前端的世界浩瀚無比啸如,小小的一章并不能很詳盡的介紹它們侍匙,僅僅是作為一個(gè)入門的介紹。
JavaScript 的解釋器 —— node 與 “模塊化”
js 和 python 同為腳本語言叮雳,他們都有自己的解釋器丈积。js 的解釋器是 node 。
在 node 出現(xiàn)前 js 有沒有自己的解釋器呢债鸡?有的,那就是我們的瀏覽器中的 js 引擎铛纬,但是這個(gè)引擎的實(shí)現(xiàn)僅僅是針對瀏覽器環(huán)境的厌均,這個(gè)引擎限制了 js 的很多功能,比如 js 在瀏覽器引擎下都不能進(jìn)行文件的讀寫告唆,當(dāng)然這么做是為了用戶的安全著想棺弊。如果我們想要用 js 實(shí)現(xiàn) python 的許多功能呢?這時(shí)就需要 node 了擒悬。
先去這里下載 node 模她,像安裝 python 一樣把 node 安裝到你的電腦上,記得把安裝路徑添加到環(huán)境變量中懂牧。這些都是和安裝 python 是一樣的侈净。
python 運(yùn)行 .py
腳本是 python <filename>.py
命令,node 也是同理僧凤, node <filename>.js
就可以運(yùn)行一個(gè) js 腳本了畜侦。
在上一章,我們在寫 index.js
時(shí)需要考慮代碼編寫的順序躯保,這是一件煩人的事情旋膳。等到以后代碼量大起來,誰知道哪個(gè)組件引用了哪個(gè)組件途事,還容易出現(xiàn) undefined 錯(cuò)誤验懊。要是我們能單獨(dú)把組件都寫在一個(gè)地方擅羞,要用他們的時(shí)候再按照需求引入就好了。也就是义图,我們希望能夠進(jìn)行“模塊化”開發(fā)减俏。不用去考慮代碼順序,做到代碼解耦歌溉。
js 被創(chuàng)建的時(shí)候并沒有考慮到模塊化開發(fā)垄懂,因?yàn)楫?dāng)時(shí)的需求還是很簡單的,隨著需求變多痛垛,模塊化開發(fā)成了必須草慧。我們知道,我們可以在 python 中使用 import 來引入我們需要的包和庫匙头。 由于在 es6 之前還沒有官方提供這個(gè)功能漫谷,于是 js 社區(qū)就自己實(shí)現(xiàn)了這項(xiàng)需求。這就是 require
和 module.exports
的故事蹂析,也就是 CommonJS 規(guī)范舔示。
在 python 中,我們直接使用 import
就可以從一個(gè)包中直接導(dǎo)入我們需要的東西电抚。但是 js 有些不同惕稻,js 需要被導(dǎo)入的包主動導(dǎo)出內(nèi)部變量,然后其它的包才能導(dǎo)入他們蝙叛。
在 CommonJS 規(guī)范中俺祠,每一個(gè)模塊都默認(rèn)含有一個(gè)全局變量 module
,它有一個(gè) exports
屬性借帘,我們可以通過這個(gè)屬性來向外暴露內(nèi)部的變量蜘渣。module.exports
的默認(rèn)值為一個(gè)空對象。外部可以通過全局的 require
函數(shù)來導(dǎo)入其它包內(nèi)的 module.exports
變量肺然。
// A.js
function out(){
console.log('model A.')
}
module.exports = out // 導(dǎo)出 out 函數(shù)
// B.js
const out = require('./A') // 從 A.js 引入 out
out()
在終端里輸入 node B.js
蔫缸,你就會看到控制臺打印出了 model A.
。
就這么簡單际起。和 Python 的差別就是 js 需要你主動導(dǎo)出變量拾碌。這也是 node 引用模塊的方法。
如果你不想寫 module.exports
加叁,還有另外一個(gè)全局變量 exports
供你使用倦沧,它是 module.exports
的引用,由于 module.exports
的默認(rèn)值為一個(gè)空對象它匕,所以它的默認(rèn)值也是一個(gè)空對象展融。如:
// 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'
有時(shí)候我們的模塊不止一個(gè)文件,而是有很多個(gè)文件豫柬。我們可以直接使用 require
來引入模塊路徑告希,require
會自動搜尋引入目錄下的 index.js
文件扑浸,它會把這個(gè)文件作為整個(gè)模塊的入口。如:
// 模塊 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'
我們在一個(gè)文件里引入:
const ucag = require('./ucag')
ucag.name // 'ucag'
ucag.age // 18
ucag.job // 'student'
在 es6 之后燕偶,js 有了自己引用模塊的方法喝噪,它有了自己的 import
和 export
關(guān)鍵字。對外導(dǎo)出用 export
指么,對內(nèi)引入用 import
酝惧。
對于導(dǎo)出,需要遵循以下語法:
export expression
// 如:
export var a = 1, b = 2; // 導(dǎo)出 a 和 b 兩個(gè)變量
export {var1, var2, ...} //var1 var2 為導(dǎo)要出的變量
export { v1 as var1, v2 as var2} // 使用 as 來改變導(dǎo)出變量的名字
不過需要注意的是伯诬,當(dāng)我們只想導(dǎo)出一個(gè)變量時(shí)晚唇,我們不能這么寫:
let a = 1;
export a; // 這是錯(cuò)誤的寫法
export { a } // 這才是正確的寫法
我們可以這樣來引入:
import { var1 }from 'model' // 從 model 導(dǎo)出 var1 變量
import {v1, v2 } from 'model' // 從 model 導(dǎo)出多個(gè)變量
import { var1 as v1 }from 'model' // 從 model 導(dǎo)出 var1 變量并命名為 v1
import * as NewVar from 'model' // 從 model 導(dǎo)入全部的變量
在使用 import
時(shí),import
的變量名要和 export
的變量名完全相同盗似,但是有時(shí)候我們我們并不知道一個(gè)文件導(dǎo)出的變量叫什么名字哩陕,只知道我們需要使用這個(gè)模塊默認(rèn)導(dǎo)出的東西,于是便出現(xiàn)了 default
關(guān)鍵字的使用赫舒。我們可以在 export
時(shí)使用這個(gè)關(guān)鍵字來做到“匿名”導(dǎo)出悍及,在 import
時(shí),隨便取個(gè)變量名就可以了接癌。
export default expression
// 如:
export default class {} // 導(dǎo)出一個(gè)類
export default {} //導(dǎo)出一個(gè)對象
export default function(){} //導(dǎo)出一個(gè)函數(shù)
我們可以這樣來引入:
import NewVar from 'model' // NewVar 是我們?yōu)?export default 導(dǎo)出變量取的名字心赶。
注意,默認(rèn)導(dǎo)出和命名導(dǎo)出各自的導(dǎo)入是有區(qū)別的:
// 默認(rèn)導(dǎo)出
export default {
name:'ucag'
}
// 默認(rèn)導(dǎo)出對應(yīng)導(dǎo)入
import AnyVarName from 'model' // 沒有花括號
AnyVarName.name // 'ucag'
//命名導(dǎo)出
export var name='ucag'
//命名導(dǎo)出對應(yīng)導(dǎo)入
import { name } from 'model' // 有花括號
name // 'ucag'
//兩種導(dǎo)出方式同時(shí)使用
export default {
name:'ucag'
}
export var age=18;
//兩種導(dǎo)入
import NameObj from 'model' //導(dǎo)入默認(rèn)導(dǎo)出
import { age } from 'model' //導(dǎo)入命名導(dǎo)出
NameObj.name // 'ucag'
age // 18
總結(jié)一下:
- 目前我們學(xué)了兩種模塊化的方式缺猛。他們是 CommonJS 的模塊化方式與 es6 的模塊化方式园担。兩種方式不要混用了哦。
- CommonJS 規(guī)范:
- 使用
module.exports
或exports
來導(dǎo)出內(nèi)部變量 - 使用
require
導(dǎo)入變量枯夜。當(dāng)被導(dǎo)入對象是路徑時(shí),require
會自動搜尋并引入目錄下的index.js
文件艰山,會把這個(gè)文件作為整個(gè)文件的入口湖雹。
- 使用
- es6 規(guī)范:
- 使用
import
與export
來導(dǎo)出內(nèi)部變量 - 當(dāng)導(dǎo)入命名導(dǎo)出變量時(shí),使用基于
import { varName } from 'model'
的語法曙搬;當(dāng)導(dǎo)入匿名或默認(rèn)導(dǎo)入時(shí)摔吏,使用import varName from 'model'
語法;
- 使用
悲催的是纵装,node 只支持 CommonJS 方式來進(jìn)行模塊化編寫代碼征讲。
前端的 pip —— npm
剛才我們講了模塊化,現(xiàn)在我們就可以用不同的模塊做很多事情了橡娄。 我們可以使用 pip 來安裝 python 的相關(guān)包诗箍,在 node 下,我們可以使用 npm 來安裝我們需要的庫挽唉。當(dāng)然滤祖,安裝包的工具不止有 npm 一種筷狼,還有許多其它的包管理工具供我們使用。現(xiàn)在的 python 已經(jīng)在安裝時(shí)默認(rèn)安裝了 pip 匠童,node 在安裝時(shí)已經(jīng)默認(rèn)安裝了 npm 埂材,所以我們就用這個(gè)現(xiàn)成的工具。
前端項(xiàng)目有個(gè)特點(diǎn) —— 版本更替特別快汤求。今天頁面是一個(gè)樣子俏险,明天可能就換成另外的樣子了,變化特別頻繁扬绪,可能今天的依賴庫是一個(gè)較低的版本竖独,明天它就更新了。所以需要把依賴的庫和項(xiàng)目放在一起勒奇,而不是全局安裝到 node 環(huán)境中预鬓。每開發(fā)一個(gè)新項(xiàng)目就需要重新安裝一次依賴庫。而真正的 node 環(huán)境下可能是什么都沒有的赊颠,就一個(gè) npm 格二。
在一個(gè)前端項(xiàng)目中,總是會把依賴庫放進(jìn)一個(gè)文件夾里竣蹦,然后從這個(gè)文件夾里導(dǎo)入需要的庫和依賴顶猜,這個(gè)文件夾叫做 node_modules
。
在 pip 中痘括,我們可以使用 requirements.txt
來記錄我們的項(xiàng)目依賴长窄。在 npm 下,我們使用 package.json
來記錄依賴纲菌。當(dāng)我們在 package.json
中寫好需要的依賴后挠日,在同一路徑下運(yùn)行 npm install
, npm 會自動搜尋當(dāng)前目錄下的 package.json
并且自動安裝其中的依賴到 node_modules
中翰舌,要是當(dāng)前目錄沒有 node_modules
目錄嚣潜,npm 就會幫我們自己創(chuàng)建一個(gè)。當(dāng)我們想要使用別人的項(xiàng)目時(shí)椅贱,直接把他們的 package.json
拷貝過來懂算,再 npm install
就可以完成開發(fā)環(huán)境的搭建了。這樣是不是特別的方便庇麦。
當(dāng)你在運(yùn)行完了 npm install
時(shí)计技,如果在以后的開發(fā)中想要再安裝新的包,直接使用 npm install <your-package>
安裝新的包就行了山橄,npm 會自動幫你把新的包裝到當(dāng)前的 node_modules
下垮媒。
在我們發(fā)布一個(gè) python 項(xiàng)目時(shí),我們對于依賴的說明通常是自己寫一個(gè) requirements.txt
,讓用戶們自己去裝依賴涣澡。 npm 為我們提供了更加炫酷的功能贱呐。在開發(fā)項(xiàng)目時(shí),你直接在含有 package.json
的目錄下運(yùn)行 npm install <your-package> --save-dev
入桂,npm 會自動幫你把依賴寫到 package.json
中奄薇。以后你就可以直接發(fā)布自己的項(xiàng)目,都不用在 package.json
中手寫依賴抗愁。
通過上面的內(nèi)容我們知道馁蒂,我們只需要在一個(gè)文件夾中創(chuàng)建好 package.json
,就可以自動安裝我們的包了蜘腌。 我們還可以使用 npm 自動生成這個(gè)文件沫屡。在一個(gè)空目錄下,運(yùn)行 npm init
撮珠,npm 會問你一些有的沒的問題沮脖,你可以隨便回答,也可以一路回車什么都不答芯急,目錄下就會自動多一個(gè) package.json
文件勺届。比如我們在一個(gè)叫做 vue-test 的路徑下運(yùn)行這個(gè)命令,記得以管理員權(quán)限運(yù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)
如果你不想按回車免姿,在運(yùn)行 npm init
時(shí)加一個(gè) -y
參數(shù),npm
就會默認(rèn)你使用它生成的答案榕酒。也就是運(yùn)行 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
后肩榕,運(yùn)行:
λ 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
此時(shí),我們發(fā)現(xiàn)又多了一個(gè) 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
蝙云。再運(yùn)行 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)為我們安裝好了依賴。
當(dāng)然,我們有時(shí)需要一些各個(gè)項(xiàng)目都會用到的工具波材。還是以 python 為例股淡,我們會使用 virtualenv
來創(chuàng)建虛擬環(huán)境,在安裝它時(shí)廷区,我們直接全局安裝到了系統(tǒng)中唯灵。npm 也可以全局安裝我們的包。在任意路徑下隙轻,使用 npm install -g <your-package>
就可以全局安裝一個(gè)包了埠帕。我們在以后會用到一個(gè)工具叫做 vue-cli
,我們可以用它來快速的創(chuàng)建一個(gè) vue 項(xiàng)目玖绿。為什么要用它呢敛瓷,在前端項(xiàng)目中,有一些庫是必須要用到的比如我們的 webpack
斑匪,比如開發(fā) vue 需要用到的 vue
包呐籽,vue-router
,vuex
等等蚀瘸,它會幫我們把這些自動寫入 package.json
中狡蝶,并且會為我們建立起最基本的項(xiàng)目結(jié)構(gòu)。就像是我們使用 django-admin
來創(chuàng)建一個(gè) Django 項(xiàng)目一樣苍姜。這樣的工具牢酵,在前端被稱為“腳手架”。
任意路徑下運(yùn)行:
npm install -g vue-cli
vue 腳手架就被安裝到了我們的 node 環(huán)境中衙猪。我們就可以在命令行中使用 vue
命令來創(chuàng)建新的項(xiàng)目了馍乙,不需要自己手動創(chuàng)建項(xiàng)目。大家可以試著運(yùn)行 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
路徑下運(yùn)行 npm run vh
职祷,你就會看到控制臺輸出了 vue 腳手架的幫助信息盟榴。
當(dāng)我們運(yùn)行 npm run <cmd>
時(shí),npm 會搜尋同目錄下的 package.json
中的 scripts
中對應(yīng)的屬性脏嚷,然后把當(dā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
:全局安裝某個(gè)包蜻懦。 -
npm run <cmd>
: 運(yùn)行當(dāng)前目錄下package.json
的scripts
中的命令。
前端工具鏈
前端開發(fā)會用到許許多多的工具夕晓,有的工具是為了更加方便的開發(fā)而生宛乃,有的工具是為了使代碼更好的適應(yīng)瀏覽器環(huán)境。每個(gè)工具的出現(xiàn)都是為了解決特定的問題运授。
解決版本差異 —— babel
版本差異一直是個(gè)很讓人頭痛的問題烤惊。用 python2 寫的代碼,大概率會在 python3 上運(yùn)行失敗吁朦。 js 是運(yùn)行在瀏覽器上的柒室,很多的瀏覽器更新并沒有能夠很穩(wěn)定的跟上 js 更新的步伐,有的瀏覽器只支持到 es5 逗宜,或者只支持部分 es6 特性雄右。為了能夠向下兼容,大家想了辦法 —— 把 es6 的代碼轉(zhuǎn)換為 es5 的代碼就行了纺讲!開發(fā)的時(shí)候用 es6 擂仍,最后再把代碼轉(zhuǎn)換成 es5 代碼就行了!于是便出現(xiàn)了 babel 熬甚。
創(chuàng)建一個(gè)叫做 babel-test
的文件夾逢渔,在此路徑下運(yùn)行:
npm init -y
npm install --save-dev babel-cli
在使用 babel 前,我們需要通過配置文件告訴它轉(zhuǎn)碼規(guī)則是什么乡括。babel 默認(rèn)的配置文件名為 .babelrc
肃廓。
在 babel-test
下創(chuàng)建 .babelrc
,寫入:
{
"presets": [
"es2015"
],
"plugins": []
}
轉(zhuǎn)碼規(guī)則是以附加規(guī)則包的形式出現(xiàn)的诲泌。所以在配置好了之后我們還需要安裝規(guī)則包盲赊。
npm install --save-dev babel-preset-es2015
babel 是以命令行的形式使用的,最常用的幾個(gè)命令如下:
# 轉(zhuǎn)碼結(jié)果打印到控制臺
babel es6.js
# 轉(zhuǎn)碼結(jié)果寫入一個(gè)文件
babel es6.js -o es5.js # 將 es6.js 的轉(zhuǎn)碼結(jié)果寫入 es5.js 中
# 轉(zhuǎn)碼整個(gè)目錄到指定路徑
babel es6js -d es5js # 將 es6js 路徑下的 js 文件轉(zhuǎn)碼到 es5js 路徑下
但是由于我們的 babel 是安裝在 babel-test 的 node_modules
中的敷扫,所以需要使用 npm run
來方便運(yùn)行以上命令哀蘑。
編輯 package.json
:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel inputs -d outputs"
}
在 babel-test 下創(chuàng)建一個(gè)新的目錄 inputs ,在其中寫入新的文件 a.js:
// es6 語法葵第,模板字符串
let name = 'ucag'
let greeting = `hello my name is ${name}`
然后運(yùn)行:
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
在一個(gè)前端項(xiàng)目中缀台,會有許許多多的文件。更重要的是栅受,最后我們需要通過瀏覽器來運(yùn)行他們将硝。我們用 es6 寫的代碼,需要轉(zhuǎn)換一次之后才能上線使用屏镊。如果我們用的是 TypeScript 寫的 js 依疼,那我們還需要先把 TypeScript 轉(zhuǎn)換為原生 js ,再用 babel 轉(zhuǎn)換為 es5 代碼而芥。如果我們使用的是模塊化開發(fā)律罢,但是瀏覽器又不支持,我們還需要把模塊化的代碼整合為瀏覽器可執(zhí)行的代碼棍丐∥蠹總之,為了方便開發(fā)與方便在瀏覽器上運(yùn)行歌逢,我們需要用到許許多多的工具巾钉。
webpack 最重要的功能就是可以把相互依賴的模塊打包。我們在模塊化開發(fā)之后秘案,可能會產(chǎn)生許多的 js 文件砰苍,要是一個(gè)個(gè)手寫把他們引入到 html 中是一件很麻煩的事情,所以我們此時(shí)就需要 webpack 來幫我們把分離的模塊組織到一起阱高,這樣就會方便很多了
創(chuàng)建一個(gè)新的路徑 webpack-test 赚导,在此路徑下運(yùn)行:
npm init -y
npm install --save-dev webpack
使用前先配置,在配置之前我們需要知道一些最基本的概念赤惊。
- 入口 entry:不管一個(gè)項(xiàng)目有多復(fù)雜吼旧,它總是有一個(gè)入口的。這個(gè)入口就被稱為 entry 未舟。這就像是我們的模塊有個(gè)
index.js
一樣圈暗。 - 出口 output:webpack 根據(jù)入口文件將被依賴的文件按照一定的規(guī)則打包在一起,最終需要一個(gè)輸出打包文件的地方处面,這就是 output 厂置。
這就是最基本的概念了,我們會在以后的教程中學(xué)習(xí)到更多有關(guān) webpack 配置的知識魂角,不過由于我們目前的需求還很簡單昵济,還用不到其它的一些功能,就算是現(xiàn)在講了也難以體會其中的作用野揪,所以我們目前不著急访忿,慢慢來。
webpack 有多種加載配置的方法斯稳,一種是寫一個(gè)獨(dú)立的配置文件海铆,一種是在命令行內(nèi)編寫配置,還有許多其它更靈活編寫配置的方法挣惰,我們以后再說卧斟。當(dāng)我們在 webpack-test 下不帶任何參數(shù)運(yùn)行 webpack
命令時(shí)殴边,webpack
會自動去尋找名為 webpack.config.js
的文件,這就是它默認(rèn)的配置文件名了珍语。
在 webpack-test 下創(chuàng)建一個(gè)新的文件 webpack.config.js
:
module.exports = {
entry:'./main.js', // 入口文件為當(dāng)前路徑下的 main.js 為文件
output:{
path:__dirname, // __dirname 是 node 中的全局變量锤岸,代表當(dāng)前路徑。
filename:'index.js' // 打包之后的文件名
}
}
編輯 package.json
:
"scripts"{
'pkg':'webpack' // 編輯快捷命令
}
以第三章的 index.js 為例板乙,當(dāng)時(shí)我們把所有的代碼都寫到了一個(gè)文件中是偷,現(xiàn)在我們可以把他們分開寫了,最后再打包起來募逞。
創(chuàng)建幾個(gè)新文件分別為 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 // 導(dǎo)出 API
store.js
const $ = require('./jquery') // 引入 jquery
let store = {
state: {
list: [],
code: '',
name: '',
id: '',
output: ''
},
actions: {
run: function (code) { //運(yùn)行代碼
$.post({
url: api.v1.run(),
data: {code: code},
dataType: 'json',
success: function (data) {
store.state.output = data.output
}
})
},
runDetail: function (id) { //運(yùn)行特定的代碼
$.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) {//獲得特定的代碼實(shí)例
$.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 // 導(dǎo)出 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">選項(xiàng)</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)">運(yùn)行</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 = {//代碼選項(xiàng)組件
template: `
<div style="display: flex;
justify-content: space-around;
flex-wrap: wrap" >
<button class="btn btn-primary" @click="run(state.code)">運(yùn)行</button>
<button class="btn btn-primary" @click="update(state.id)">保存</button>
<button class="btn" @click="update(state.id, true)">保存并運(yùn)行</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
} // 導(dǎo)出組件
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">請?jiān)谙路捷斎氪a:</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({ //根組件放接,整個(gè)頁面入口
el: '#app',
template: '<app></app>',
components: {
'app': app
}
})
在 webpack-test 下運(yùn)行:
npm run pkg # 運(yùn)行 webpack
過一會兒你就會發(fā)現(xiàn)多了一個(gè) index.js
文件刺啦,這就是我們打包的最終結(jié)果了。 6 個(gè) js 文件被打包成了一個(gè)文件透乾,最終打包的 index.js
的功能和那 6 個(gè) 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)它在控制臺報(bào)錯(cuò)了。這是 webpack 的問題嗎扳抽?這是我們之后要解決的問題篡帕。保持你的好奇心。