探索是人類的基因病线,不斷的探索新技術(shù)才是一個(gè)前端開(kāi)發(fā)人的正常狀態(tài)伺糠,下面就總結(jié)一下我們?cè)诓粩嘧兓那岸耸澜缋锶绾务{馭一種新的開(kāi)發(fā)思維伯病。
React及React生態(tài)圈那點(diǎn)東西
所謂前端柜候,直觀理解就是不斷向前發(fā)展碎连,在發(fā)展的道路上灰羽,總會(huì)有一些新的東西出現(xiàn),React就是其中之一鱼辙。
在做React項(xiàng)目的時(shí)候廉嚼,我們很可能是直接拿線上項(xiàng)目做遷移。這樣一來(lái)倒戏,對(duì)里面的枝枝葉葉就會(huì)產(chǎn)生很多疑問(wèn)怠噪,?杜跷?and why傍念。希望這個(gè)專題的文章能給你一個(gè)很好的參考作用,如果能解答你心中疑問(wèn)葛闷,那將是我最開(kāi)心的事憋槐。
tips:這個(gè)專題會(huì)把React的使用總結(jié)一遍,其中包括以實(shí)例的方式總結(jié)React(SSR)同構(gòu)應(yīng)用開(kāi)發(fā)的全過(guò)程
這篇的主題—前端處理模塊化的現(xiàn)狀
提前說(shuō)明淑趾,“騰訊智推”項(xiàng)目是使用ES2015標(biāo)準(zhǔn)及Webpack做構(gòu)建工具開(kāi)發(fā)的阳仔。如果還沒(méi)使用過(guò)的同學(xué),別擔(dān)心扣泊,文中我會(huì)說(shuō)明為什么使用及如何使用驳概,可以說(shuō)你躺著也能學(xué)會(huì)了巧鸭,而且保證你會(huì)愛(ài)上它闯估。所以ES2015 、組件化少梁、Webpack......都屬于需要提前準(zhǔn)備的技術(shù)等孵。
本文的閱讀大綱:
1稚照、ES6正在彌補(bǔ)JS語(yǔ)言上的缺陷
2、聊一聊前端組件、包管理果录、構(gòu)建
3上枕、webpack怎么使用
4、最后是總結(jié)
一弱恒、ES6正在彌補(bǔ)JS語(yǔ)言上的缺陷嗎辨萍?
【不信的話,請(qǐng)您住下閱讀】
===const返弹、let關(guān)鍵字===
全端人民都知道锈玉,在JavaScript中,變量默認(rèn)是全局性的义起,只存在函數(shù)級(jí)作用域拉背,聲明函數(shù)曾經(jīng)是創(chuàng)作作用域的唯一方式。也就是講默终,在前端娃娃的腦海里是不存在塊級(jí)作用域的椅棺,像if/else等這些“籠子”是根本關(guān)不住變量這些“鳥(niǎo)”的,然而齐蔽,ES6標(biāo)準(zhǔn)中let
修復(fù)了JS中的這一缺陷两疚。
看個(gè)栗子
if(true) {
let a = 'wenping';
}
console.log(a); // a is not defined
沒(méi)有問(wèn)題,結(jié)果和意想的結(jié)果一樣含滴,也就證明了包含let
定義變量a
的語(yǔ)句形成了一個(gè)塊級(jí)作用域诱渤。大家應(yīng)該都知道,ES6還有個(gè)定義語(yǔ)句叫const
蛙吏,有了個(gè)let
源哩,干嘛還要制定一個(gè)const鞋吉?其實(shí)const
是用來(lái)定義一個(gè)常量鸦做,一旦定義以后不可以修改,但如果是引用類型的話谓着,可以改變它的屬性泼诱。什么意思呢?來(lái)看個(gè)例子:
const myname = 'wenping';
myname = 'wp'; // "CONSTANT" is read-only
const myname = {foo:'wenping'};
myname.foo = 'wp'; // 正常運(yùn)行
===函數(shù)===
1赊锚、箭頭函數(shù)永遠(yuǎn)是匿名的治筒,......
看個(gè)栗子就明白了
let add = (a,b) => {return a+b};
// 等同于
let add = function(a, b){
return a+b;
}
2、this在剪頭函數(shù)中指向的是親爹舷蒲,......
比如:
let age = 2;
let kitty = {
age:1,
grow:function(){
setTimeout(function(){
console.log(++this.age);
},100)
}
}
kitty.grow();// 3
若是想得到2的結(jié)果耸袜,大家可能都這么去做,使用hack(在grow方法中定義一個(gè)const self = this,console.log中變成++self.age)
然而牲平,當(dāng)有了ES6的箭頭函數(shù)堤框,一切都變的簡(jiǎn)單多了。
上面代碼的相同功能實(shí)現(xiàn)簡(jiǎn)寫如下:
let kitty = {
age:1,
grow:function() {
setTimeout(() => {
console.log(++this.age); // 2
},100);
}
}
是不是很簡(jiǎn)潔、簡(jiǎn)單蜈抓、簡(jiǎn)直启绰、簡(jiǎn)略、簡(jiǎn).....
3沟使、函數(shù)默認(rèn)參數(shù)定義一步到位委可,......
當(dāng)一個(gè)函數(shù)所傳的參數(shù)不確定是否存在的時(shí)候,一般我們會(huì)往函數(shù)開(kāi)頭"塞"一行這樣的代碼
value = value || []
當(dāng)程序變得復(fù)雜之后腊嗡,這種hack也就成了一種心里負(fù)擔(dān)着倾。
然而,ES6很輕松的處理了這個(gè)問(wèn)題
function desc(name = 'Peter',age=5) {
return name + 'is' + age + 'years old';
}
desc();// Peter is 5 years old
關(guān)于函數(shù)還有一個(gè)"三點(diǎn)"隱形傳參的事叽唱, 所謂的“三點(diǎn)”在ES6中稱展開(kāi)操作符屈呕,下面會(huì)詳細(xì)一下。
===展開(kāi)操作符===
1棺亭、函數(shù)調(diào)用變的簡(jiǎn)單虎眨,......
在JS中想讓一個(gè)函數(shù)把一個(gè)數(shù)組依次作為參數(shù)調(diào)用,很可能就是這么去寫:
function test(x,y,z){
console.log(x,y,z);
}
let arr = [1,2,3];
test.apply(null,arr);
// 這個(gè)時(shí)候x,y,z分別輸出1镶摘、2嗽桩、3
看看這個(gè)問(wèn)題ES6是怎么處理的。
test(...arr);
樓上妹子表情有點(diǎn)夸張凄敢,效果僅當(dāng)參考......
2碌冶、合并數(shù)組有新招,......
三點(diǎn)改變了處理數(shù)組的方式涝缝,比如你要組建一個(gè)具有新元素的數(shù)組扑庞,前端的我們都會(huì)想到用切分(splice)、合并(concat)拒逮、塞入(push)罐氨。
看一下ES6如何處理
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr3 = [...arr1, ...arr2];
console.log(arr3);
// [1,2,3,4,5,6]
呵呵,覺(jué)得簡(jiǎn)單的可以點(diǎn)個(gè)贊滩援。
===字符串拼接更清晰===
來(lái)看看這個(gè)讓我端人員看著別扭的栅隐,每次都要想辦法寫的盡量好看的字符串拼接問(wèn)題。ES6是這樣處理的:
變量拼接:
let name = 'wpzheng';
let a = `My name is ${name}`;
console.log(a);// My name is wpzheng
折行串拼接:
let a = `My name is wpzheng
My name is wpzheng
My name is wpzheng`;
console.log(a);// My name is wpzheng*3
雖然以上一憋氣寫了這么多玩徊,但是ES6貌似還有個(gè)精華點(diǎn)沒(méi)有提到......
$類和模塊$
$代表價(jià)值租悄, 通俗的講就是錢。值得重點(diǎn)分析一下的東西我都習(xí)慣用$包起來(lái)恩袱∑澹——這句話好像有點(diǎn)多余。
其實(shí)我想說(shuō)類和模塊的理解對(duì)后面理解react編碼非常重要畔塔,畢竟這里總結(jié)ES6并非因?qū)W習(xí)ES6潭辈,而是理解和熟悉
===類和模塊===
眾人都知纪吮,在JavaScript的世界里是沒(méi)有傳統(tǒng)類的概念的。它使用原型鏈的方式來(lái)完成繼承萎胰。而這種方式總讓人覺(jué)得怪怪的碾盟。沒(méi)有class來(lái)的清晰易理解。
ES6用class定義類實(shí)現(xiàn)繼承如下:
類
class Animal() {
// 構(gòu)造函數(shù)
constructor(name, age) {
this.name = name;
this.age = age;
}
// 公有方法
shout() {
return `My name is ${this.name}, age is ${this.age}`;
}
// 靜態(tài)方法
static foo() {
return `Here is a static method`;
}
}
// 使用
const cow = new Animal('betty',2);
cow.shout();
// My name is betty, age is 2
Animal.foo();
//Here is a static method
繼承
class Dog extends Animal {
constructor(name,age = 2,color='black') {
//在構(gòu)造函數(shù)中直接調(diào)用 super方法
**super(name,age);**
this.color = color;
}
shout() {
return**super.shout()**+ `,color is ${this.color}`;
}
}
const jackTheDog = new Dog('jack');
jackTheDog.shout();
//"My name is jack,age is 2,color is black"
這里我認(rèn)為主要要理解的是的super方法技竟,其它都似曾相識(shí)冰肴。我的理解是,super指的是父元素的引用榔组,相當(dāng)于父元素的this熙尉。構(gòu)造函數(shù)中使用super相當(dāng)于實(shí)現(xiàn)了繼承。在非構(gòu)造函數(shù)中不能使用super方法搓扯,但可以采用super(). + 方法名字調(diào)用父類的方法检痰。
前端模塊概念
我是這么理解的,正因?yàn)榍岸藰I(yè)務(wù)變的越來(lái)越復(fù)雜锨推,促使前端越來(lái)越趨于工程化的開(kāi)發(fā)铅歼,模塊就是工程化的最小單元,記得一年前换可,我們做項(xiàng)目都使用Require.js椎椰,它所推崇的是AMD格式開(kāi)發(fā),之后開(kāi)始研究Node.js沾鳄,它使用的是CommonJS格式慨飘,當(dāng)然,不管使用的是哪種格式译荞,它們都是以模塊為單元進(jìn)行開(kāi)發(fā)的瓤的,再后來(lái),出現(xiàn)了browserify吞歼、webpack圈膏。這兩個(gè)工具的作用就是將模塊化的JS進(jìn)行編譯、轉(zhuǎn)換浆熔、合并本辐,最后生成瀏覽器能夠解析的代碼桥帆。由此医增,我們來(lái)看一下,ES6模塊化JS怎么定義的:
一個(gè)導(dǎo)出:
// hello.js
function hello(){
console.log('hello');
}
export hello;
// main.js
import { hello } from './hello';
hello();// hello
多個(gè)導(dǎo)出:
// hello.js
export const PI = 3.14;
export function hello(){
console.log('hello');
}
export let preson = {name:'viking'};
// main.js
導(dǎo)出可以這么寫
import {PI, hello, person} from './hello';
也可以這么寫
import * as util from './hello';
console.log(util.PI);
默認(rèn)導(dǎo)出:
ES6使用default關(guān)鍵字來(lái)實(shí)現(xiàn)模塊的默認(rèn)導(dǎo)出
// hello.js
export default function(){
console.log('hello ES6');
}
// main.js
import hello from './hello';
hello();// hello ES6;
到這里老虫,ES6要說(shuō)的叶骨,差不多寫完了,如果覺(jué)得不完整不詳細(xì)祈匙,那就對(duì)了忽刽,因?yàn)榇似皇亲鳛榭偨Y(jié)React的開(kāi)頭篇
做人有野心天揖,才能知其難為而勉力為之,做人有野心跪帝,才能知其必為而拼力為之今膊,做人野心,才能知其可為而全力為之......
【使用Babel】
在JavaScript這門語(yǔ)言不斷發(fā)展同時(shí)伞剑,它需要面對(duì)的一個(gè)事實(shí)是瀏覽器的多樣性存在斑唬。新的語(yǔ)語(yǔ)言寫法(其實(shí)就是指ES6)不一定會(huì)被某些瀏覽器認(rèn)識(shí),所以就有了這些中間件(這里說(shuō)的是Babel)黎泣,它的作用也就很明顯了恕刘,就是一個(gè)JS編譯器,把ES6代碼編譯成ES5代碼抒倚。好處是我們可以快樂(lè)輕松的在代碼世界里使用ES6詼諧剩下人生中的每一行代碼 褐着。
二、聊一聊前端組件托呕、包管理含蓉、構(gòu)建
【組件】
先區(qū)分一下模塊module和組件component。
此文上面談到了JavaScript的模塊寫法项郊,由此可以看出谴餐,模塊大多指是的語(yǔ)言層面的,往往表現(xiàn)為一個(gè)單獨(dú)的JS文件呆抑,對(duì)外暴露一些屬性和方法岂嗓。
而前端組件則更多的是指業(yè)務(wù)層面的,可以看成是一個(gè)可以獨(dú)立使用的功能鹊碍。組件可能是一個(gè)模塊厌殉,也可能是多個(gè)模塊,總之侈咕,組件化是以模塊化方案為基礎(chǔ)公罕。
正因?yàn)楝F(xiàn)在的打包工具(browserify、webpack)允許我們將一般的資源視作與JavaScript平等的模塊耀销,并以一致的方式加載進(jìn)來(lái)楼眷。所以我們組件化開(kāi)發(fā)的項(xiàng)目結(jié)構(gòu)可以這樣去組織:
foo/
- img/
- index.js
- style.scss
bar/
...
其中index.js如下
require('./style.less');
const bar = require('./bar);
module.exports = function(){
......
}
【包管理】
前端圍繞著node,開(kāi)始搞全棧熊尉。其中npm作為目前前端最為流行的包管理工具罐柳,深受大家的喜愛(ài),前端幾乎所有的框架和庫(kù)在其上面都有注冊(cè)狰住。同時(shí)张吉,許多瀏覽器場(chǎng)景應(yīng)用的包,也可以使用CommonJS或者ES6模塊開(kāi)發(fā)了催植。
本項(xiàng)目下安裝
npm install lodash
當(dāng)命令運(yùn)行完畢以后肮蛹,它會(huì)在當(dāng)前目錄下生成一個(gè)node_moudle文件夾勺择,并且將要安裝的模塊下載到這個(gè)文件夾中。
全局安裝
還有一類命令行工具類的模塊你需要進(jìn)行全局安裝
npm install -g jshint
在實(shí)際項(xiàng)目中伦忠,往往依賴的不只是一個(gè)包省核,而是多個(gè)包,這個(gè)時(shí)候就可以寫一個(gè)配制文件昆码,然后執(zhí)行npm install統(tǒng)一安裝芳撒。這個(gè)配制文件叫package.json。
這個(gè)配制里可以定義兩個(gè)類型的包:
dependencies: 在生產(chǎn)環(huán)境中需要依賴的包未桥。
devDependencies:僅在開(kāi)發(fā)和測(cè)試環(huán)節(jié)需要依賴的包笔刹。
這里可以手動(dòng)往package.json添加這些安裝包信息,然后統(tǒng)一安裝冬耿,也可以使用命令行一個(gè)個(gè)安裝:
npm install XXX --save // 生產(chǎn)
npm install XXX --save-dev // 開(kāi)發(fā)和測(cè)試
【構(gòu)建】
在前端項(xiàng)目中會(huì)遇到各種各樣的任務(wù)舌菜,比如說(shuō)壓縮合并代碼,驗(yàn)證代碼格式亦镶,測(cè)試代碼日月,監(jiān)視文件是否有變化等。這些任務(wù)構(gòu)建工具能幫我們處理好缤骨。
記得兩年前我們都使用grunt作用構(gòu)建工具爱咬,grunt具有非常完善的插件機(jī)制。配置文件Gruntfile.js核心大概是這樣的:
grunt.initConfig({
jshint:{
src:‘src/test.js'
}绊起,
uglify:{
build:{
src:‘src/test.js’精拟,
dest:‘build/test.min.js'
}
}
})
行業(yè)總是在不斷的變化,總會(huì)有更好的東西來(lái)取代現(xiàn)有的方案虱歪,沒(méi)有最好只有更好蜂绎,說(shuō)的就是一種不斷超越的精神。
gulp吸取了Grunt優(yōu)點(diǎn)笋鄙,通過(guò)流的(Stream)的概念來(lái)簡(jiǎn)化多個(gè)任務(wù)之間的配置和輸出师枣。配置文件gulpfile.js核心大概是這樣的:
var gulp = require('gulp');
var uglify = require('gulp-uglify');
// 定義compress任務(wù),壓縮代碼
gulp.task('compress', function(){
return gulp.src('src/test.js')
.pipe(uglify())
.pipe(gulp.dest('build'));
})
gulp.task('default',['compress']);
相比grunt,gulp配置更簡(jiǎn)單萧落,并且實(shí)現(xiàn)更清晰明了践美。
同時(shí),正因?yàn)橛辛四K化打包工具(browserify找岖、webpack),將瀏覽器不支持的模塊進(jìn)行編譯陨倡、轉(zhuǎn)換、合并宣增,并且最后生成瀏覽器可以運(yùn)行的代碼玫膀,才使我們能夠毫無(wú)顧慮的使用es6的模塊化編程矛缨。
webpack支持AMD和CommonJS類型爹脾,通過(guò)loader機(jī)制可以使用es6的模塊格式帖旨。當(dāng)然,它也提供了非常豐富的功能灵妨,接下來(lái)我會(huì)專門談一下我對(duì)webpack的理解解阅,希望對(duì)大家有用。
三泌霍、webpack怎么玩
要總結(jié)一個(gè)工具货抄,我覺(jué)得最重要的一點(diǎn)是說(shuō)清楚它能做什么,怎么做朱转。
正因?yàn)榍岸隧?xiàng)目變得越來(lái)越復(fù)雜蟹地,構(gòu)建系統(tǒng)已成了一個(gè)不可或缺的部分,而模塊打包正是構(gòu)建系統(tǒng)的核心藤为。webpack正是一個(gè)為前端模塊打包構(gòu)建而生的工具怪与。它既吸取了大量已有方案的優(yōu)點(diǎn)和教訓(xùn),也解決了很多前端開(kāi)發(fā)過(guò)程中已存在的痛點(diǎn)缅疟,如代碼的拆分與異步加載分别、對(duì)非JavaScript資源的支持等。(這里現(xiàn)有方案主要是指RequireJS和browserify)
===安裝===
webpack安裝很簡(jiǎn)單存淫,由于在項(xiàng)目中webpack只是一個(gè)構(gòu)建工具的角色耘斩,不是代碼依賴,所以應(yīng)該安裝在dev-dependencies中桅咆。
*npm install webpack --save-dev*
假如模塊文件hello.js如下:
exports.modles = "hello world!";
假如項(xiàng)目的入口文件index.js內(nèi)容如下:
var text = require('./hello');
console.log(text);
通過(guò)webpack生成瀏覽器認(rèn)識(shí)的我們最終引入到主index.html頁(yè)中的文件bundle.js括授。
webpack ./index.js bundle.js
因?yàn)椋@個(gè)模塊文件比較簡(jiǎn)單岩饼, 我們可以打開(kāi)bundle.js文件看一下生成之后的代碼刽脖,由此你就會(huì)發(fā)現(xiàn)在,webpack其實(shí)就是做了兩部分工作:
1忌愚、分析得到所有必需模塊并合并(甚至壓縮)曲管。
2、提供這些模塊有序和正常執(zhí)行的環(huán)境硕糊。
webpack之所以功能強(qiáng)大院水,其中重要原因之一是它有強(qiáng)大的loaders和plugin
看一下官網(wǎng)對(duì)loader的解釋
寶寶愣了,這是啥呀简十?檬某??螟蝙?恢恼?
loaders是作用于應(yīng)用中資源文件的轉(zhuǎn)換行為。它們是函數(shù)(運(yùn)行在Node.js環(huán)境中)胰默,接收資源文件的源代碼作用參數(shù)场斑,并返回新的代碼漓踢。舉個(gè)例子,你可以通過(guò)jsx-loader將React的JSX代碼轉(zhuǎn)換為JS代碼漏隐,從而可以被瀏覽器執(zhí)行喧半。
就寶寶這表情,下面我寫個(gè)例子:
// start
先安裝兩個(gè)cssloader青责。
npm install style-loader css-loader --save-dev
編輯index.css代碼
div{
width:100px;
height:100px;
background-color:red;
}
編輯index.js代碼碼
require('style!css!./index.css'); // xxx!表示指定特定的loader
document.body.appendChild(document.createElement('div'));
執(zhí)行命令webpack ./index.js bundle.js
最后在入口文件.html中引入bundle.js看是否起作用挺据。
// end
很明顯上面這種指定xxx!的做法不科學(xué),所以接下來(lái)說(shuō)一下webpack配制文件package.json脖隶。
說(shuō)一下插件(plugin):
插件的存在可以看成是為了實(shí)現(xiàn)那些loader實(shí)現(xiàn)不了或不適合在loader中實(shí)現(xiàn)的功能扁耐,比如,自動(dòng)生成項(xiàng)目的HTML頁(yè)面(HtmlWebpackPlugin)等产阱。
插件分webpack內(nèi)置和獨(dú)立安裝包兩種形式做葵,內(nèi)置的插件可以通過(guò)webpack直接引用。獨(dú)立安裝包的插件需要先通過(guò)npm安裝心墅,然后在配制文件里引入酿矢。
如:
npm i html-webpack-plugin@1.7.0 --save-dev // 如果不帶@默認(rèn)會(huì)安裝最新版本
在配制文件webpack.config.js中引入
var HtmlWebpackPlugin = require('html-webpack-plugin');
===配制===
webpack支持Node.js
模塊格式的配置文件,默認(rèn)會(huì)使用當(dāng)前目錄下的webpack.config.js怎燥,配置文件只需要export的一個(gè)配置信息對(duì)象即可瘫筐,如下
module.exports = {
}
配置文件內(nèi)容大概就是輸入、輸出铐姚、loaders策肝、插件引入。
===運(yùn)行===
在開(kāi)發(fā)的時(shí)候隐绵,如果每一次小的改動(dòng)都要手動(dòng)執(zhí)行一遍構(gòu)建才能看到效果的話之众,那開(kāi)發(fā)就會(huì)變得很煩瑣。
webpack提供了兩種方式來(lái)時(shí)時(shí)響應(yīng)變化依许。
1棺禾、watch:
運(yùn)行webpack -w
通過(guò)添加--watch
選項(xiàng)即可開(kāi)啟監(jiān)視功能,webpack會(huì)首先進(jìn)行一次構(gòu)建峭跳,然后依據(jù)構(gòu)建得到的依賴關(guān)系膘婶,對(duì)項(xiàng)目所依賴的所有文件進(jìn)行監(jiān)聽(tīng),一旦發(fā)生改動(dòng)則觸發(fā)重新構(gòu)建蛀醉。
2悬襟、webpack-dev-server
使用webpack-dev-server需要額外安裝webpack-dev-server包。
npm install webpack-dev-server -g
然后直接啟動(dòng)
webpack-dev-server
webpack-dev-server默認(rèn)會(huì)監(jiān)聽(tīng)8080端口拯刁,因此直接在瀏覽器里打開(kāi)http://localhost:8080脊岳,即可看到結(jié)果頁(yè)面。
四、總結(jié)
全篇就是圍繞前端模塊化的主題展開(kāi)總結(jié)的割捅,講了ES6奶躯、前端打包工具(webpack)等,這些內(nèi)容在今后寫React總結(jié)的時(shí)候棺牧,都會(huì)用到巫糙。這篇就到這了朗儒,以上如有總結(jié)的不對(duì)的對(duì)方颊乘,歡迎留言指正。