前端基礎(chǔ)

前端基礎(chǔ)

一、ES6

1.1 ES6簡(jiǎn)介

ECMAScript 6.0(以下簡(jiǎn)稱(chēng)ES6,ECMAScript是一種有Ecma國(guó)際(前身為歐洲計(jì)算機(jī)制造商協(xié)會(huì),英文名是European Computer Manufacturers Association)通過(guò)ECMA-2262標(biāo)準(zhǔn)化的腳本程序設(shè)計(jì)語(yǔ)言)是 JavaScript 語(yǔ)言的下一代標(biāo)準(zhǔn),已經(jīng)在2015年6月正式發(fā)布了,并且從ECMAScript 6 開(kāi)始,開(kāi)始采用年號(hào)來(lái)做版本,即ECMAScript 2015,就是ECMAScript 6。它的目標(biāo),是使得JavaScript語(yǔ)言可以用來(lái)編寫(xiě)復(fù)雜的大型應(yīng)用程序,稱(chēng)為企業(yè)級(jí)開(kāi)發(fā)語(yǔ)言。每年會(huì)出一個(gè)新版本。

1.2 ES6新特性

1.2.1 聲明變量

1.2.1.1 let聲明變量

特性1:

var聲明的變量往往會(huì)跨域

let聲明的變量有嚴(yán)格局部作用域

{
    var a = 1;
    let b = 2;
}
console.log(a);  // 在網(wǎng)頁(yè)控制臺(tái)能看到1
console.log(b);  // ReferenceError: b is not defined

特性2:

var可以多次聲明變量

let只能聲明一次

特性3:

var會(huì)變量提升

let不存在變量提升

console.log(a);  // undefind (不算報(bào)錯(cuò))
var a = 1;
console.log(b);  // ReferenceError: b is not defined (報(bào)錯(cuò))
let b = 2;

1.2.2.2 const聲明常量(只讀變量)

  1. const聲明后不允許改變
  2. 一旦用const聲明必須初始化,否則會(huì)報(bào)錯(cuò)
const a = 1;
a = 3;  // Uncaught TypeError: Assignment to constant variable

1.2.2 解構(gòu)表達(dá)式

1.2.2.1 數(shù)組解構(gòu)

let arr = [1,2,3];
// 舊方法:
let a = arr[0];
let b = arr[1];
let c = arr[2];
// 解構(gòu)方法:
let [a, b, c] = arr;
console.log(a, b, c);  // 兩種方法都能打印成功

1.2.2.2 對(duì)象解構(gòu)

const person = {
    name: "jack",
    age: 21,
    language: ['java', 'js', 'css']
}
// 舊方法:
const name = person.name;
const age = person.age;
const language = person.language;
// 解構(gòu)方法:
const {name: abc, age, language} = person;
console.log(abc, age, language);  // 兩種方法都能打印成功

1.2.3 字符串?dāng)U展

1.2.3.1 幾個(gè)新的API

ES6為字符串?dāng)U展了幾個(gè)新的API:

  • str.includes("xxx"),返回布爾值,表示是否找到了參數(shù)字符串
  • str.startsWith("xxx"),返回布爾值,表示參數(shù)字符串是否在原字符串頭部
  • str.endsWith("xxx"),返回布爾值,表示參數(shù)字符串是否在原字符串尾部
let str = "hello.vue";
console.log(str.includes("e"));  // true
console.log(str.includes("hello"));  // true
console.log(str.startsWith("hello"));  // true
console.log(str.endsWith(".vue"));  // true

1.2.3.2 字符串模板

模板字符串相當(dāng)于加強(qiáng)版的字符串,用反引號(hào)"`",除了作為普通字符串,還可以用來(lái)定義多行字符串,還可以在字符串中加入變量、表達(dá)式和函數(shù)。

特性1:多行字符串。如果是舊方法只能將多個(gè)字符串拼接。

let ss = `<div>
    <span>hello world</span>
</div>`;
console.log(ss);

特性2:字符串插入變量和表達(dá)式。變量名寫(xiě)在 {} 中,{} 中可以放入 JavaScript 表達(dá)式。

let name = "張三";
let age = 18;
let info = `我是${name}, 今年${age + 5}歲了??!`;
console.log(info);

特性3:字符串中調(diào)用函數(shù)

function fun() {
    return "這是一個(gè)函數(shù)";
}
let info = `字符串調(diào)用了函數(shù):${fun()}`;
console.log(info);

1.2.4 函數(shù)優(yōu)化

1.2.4.1 函數(shù)參數(shù)默認(rèn)值

在 ES6 以前,我們無(wú)法給一個(gè)函數(shù)參數(shù)設(shè)置默認(rèn)值,只能采用變通寫(xiě)法

function add(a, b) {
    // 判斷b是否為空, 如果為空則給默認(rèn)值1
    b = b || 1;
    return a + b;
}
console.log(add(10));

在 ES6 中可以直接給參數(shù)寫(xiě)上一個(gè)默認(rèn)這,沒(méi)傳就會(huì)自動(dòng)使用默認(rèn)值

function add(a, b = 1) {
    return a + b;
}
console.log(add(10));

1.2.4.2 不定參數(shù)

不定參數(shù)用來(lái)表示不確定參數(shù)個(gè)數(shù)。形如:...變量名,由 ... 加上一個(gè)具名參數(shù)標(biāo)識(shí)符組成。具名參數(shù)只能放在參數(shù)列表的最后,并且有且只有一個(gè)不定參數(shù)

function fun(...values) {
    console.log(values.length);
}
fun(1, 2);  // 打印2
fun(1, 2, 3, 4);  // 打印4

1.2.4.3 箭頭函數(shù)

ES6 中定義函數(shù)的簡(jiǎn)寫(xiě)方式

  1. 定義的方法只有一個(gè)參數(shù)、一行方法體時(shí)
<script>
    // 以前聲明一個(gè)方法:
    var print = function (obj) {
        console.log(obj);
    }
    // 使用箭頭函數(shù)聲明方法:
    var print = obj => console.log(obj);
    print("hello!");
</script>
  1. 定義的方法有多個(gè)參數(shù)、一行方法體時(shí)
// 以前聲明:
var sum = function (a, b) {
    return a + b;
}
// 使用箭頭函數(shù)聲明:
var sum = (a, b) => a + b;
console.log(sum(10,20));
  1. 定義的方法有多個(gè)參數(shù)、多行方法體時(shí)
// 以前聲明:
var sum = function (a, b) {
    c = a + b;
    return a + c;
}
//使用箭頭函數(shù)聲明:
var sum = (a, b) => {
    c = a + b;
    return a + c;
}
console.log(sum(10, 20));

1.2.4.4 實(shí)戰(zhàn):箭頭函數(shù)結(jié)合解構(gòu)表達(dá)式

// 需求: 聲明一個(gè)對(duì)象, hello方法需要對(duì)象的個(gè)別屬性
// 以前的方式:
const person = {
    name: "jack",
    age: 21,
    language: ['java', 'js', 'css']
}
function hello(obj) {
    console.log("hello, " + obj.name);
}
hello(person);

// 箭頭函數(shù) + 解構(gòu)表達(dá)式的寫(xiě)法:
var hello = ({name}) => console.log("hello, " + name);  // 在方法入?yún)⒌臅r(shí)候解構(gòu)出傳入的對(duì)象的name屬性
hello(person);

1.2.5 對(duì)象優(yōu)化

1.2.5.1 新增的API

ES6給Object拓展了許多新的方法,比如:

  • keys(obj):獲取對(duì)象的所有key形成的數(shù)組
  • values(obj):獲取對(duì)象的所有value形成的數(shù)組
  • entries(obj):獲取對(duì)象的所有key和value形成的二維數(shù)組。格式:[[k1, v1], [k2, v2], ...]
  • assign(dest, ...src):將多個(gè)src對(duì)象的值拷貝到dest中。(第一層為淺拷貝,第二層為深拷貝:如果果對(duì)象的屬性值為簡(jiǎn)單類(lèi)型(如string, number),通過(guò)Object.assign({},srcObj);得到的新對(duì)象為深拷貝;如果屬性值為對(duì)象或其它引用類(lèi)型,那對(duì)于這個(gè)對(duì)象而言其實(shí)是淺拷貝的)

深拷貝和淺拷貝:

深拷貝和淺拷貝最根本的區(qū)別在于是否真正獲取一個(gè)對(duì)象的復(fù)制實(shí)體,而不是引用。

假設(shè)B復(fù)制了A,修改A的時(shí)候,看B是否發(fā)生變化:

如果B跟著**也變了**,說(shuō)明是淺拷貝,拿人手短?。ㄐ薷亩褍?nèi)存中的同一個(gè)值)

如果B**沒(méi)有改變**,說(shuō)明是深拷貝,自食其力?。ㄐ薷亩褍?nèi)存中的不同的值)
const person = {
    name: jack,
    age: 21,
    language: ['java', 'js', 'css']
}

console.log(Object.keys(person));  // ["name", "age", language]
console.log(Object.values(person));  // ["jack", 21, Array(3)]
console.log(Object.entries(person));  // [Array(2), Array(2), Array(2)]

const target = {a: 1}
const source1 = {b: 2}
const source2 = {c: 3}
Object.assign(target, source1, source2);
console.log(target);  // {a: 1, b: 2, c: 3}

1.2.5.2 聲明對(duì)象簡(jiǎn)寫(xiě)

const name = "張三";
const age = 23;

// 以前的寫(xiě)法:
const person = {name: name, age: age}  // {name: "張三", age: 23}

// ES6簡(jiǎn)寫(xiě)的方法: 當(dāng)對(duì)象中屬性名與賦值的變量名相同時(shí)可以這么寫(xiě)
const person = {name, age}  // {name: "張三", age: 23}

1.2.5.3 對(duì)象函數(shù)的簡(jiǎn)寫(xiě)

let person = {
    name: jack,
    
    // 以前的寫(xiě)法:
    eat: function(food) {
        console.log(this.name + "在吃" + food);
    },
    
    // 箭頭函數(shù)的寫(xiě)法:
    // 注意: 箭頭函數(shù)不能使用this, 必須用 對(duì)象.屬性
    eat2: food => console.log(person.name + "在吃" + food),
    
    // 非箭頭函數(shù)的簡(jiǎn)寫(xiě)方法:
    eat3(food) {
        console.log(this.name + "在吃" + food);
    }
}

person.eat("香蕉");  // jack在吃香蕉
person.eat2("蘋(píng)果");  // jack在吃蘋(píng)果
person.eat3("橘子");  // jack在吃橘子

1.2.5.4 對(duì)象的拓展運(yùn)算符

拓展運(yùn)算符 “...” 用于取出參數(shù)對(duì)象所有可遍歷的屬性然后拷貝到當(dāng)前對(duì)象

  1. 拷貝對(duì)象(深拷貝)
let person = {name: "Amy", age: 15}
let someone = {...person}  // 把person這個(gè)對(duì)象中的屬性全部拆分出來(lái)
console.log(someone);  // {name: "Amy", age: 15}
  1. 合并對(duì)象
let name = {name: "Amy"}
let age = {age :15}
let person = {name: "Zhangsan"}
person = {...name, ...age}  // 如果兩個(gè)對(duì)象的屬性名重復(fù), 后面對(duì)象的屬性值會(huì)覆蓋前面對(duì)象的屬性值
console.log(person);  // {name: "Amy", age: 15} "Amy"把person中原來(lái)的"Zhangsan"覆蓋掉了

1.2.6 類(lèi)定義優(yōu)化

1.2.6.1 ES5中的類(lèi)定義

// 在ES5中使用function來(lái)定義類(lèi)
function Shape() {
    // 私有屬性
    var x = 1;
    var y = 2;
    // 公有屬性
    this.x_public = 1;
    this.y_public = 2;
    /*
        在Javascript中,函數(shù)是Function類(lèi)的實(shí)例,F(xiàn)unction間接繼承自O(shè)bject,
        所以,函數(shù)也是一個(gè)對(duì)象,因此,我們可以用賦值的方法創(chuàng)建函數(shù),
        當(dāng)然,我們也可以將一個(gè)函數(shù)賦給類(lèi)的一個(gè)屬性變量
    */
    // 靜態(tài)屬性、靜態(tài)方法:是屬于類(lèi)的, 不是屬于對(duì)象的
    Shape.z = 3;
    Shape.show_static() {
        console.log("靜態(tài)方法")
    }
    // 同樣, 使用var定義的方法為私有方法, 用this.來(lái)定義的方法為公有方法
    var show = function() {
        console.log("我是私有show方法");
    }
    this.show_public = function() {
        console.log("我是公有show方法")
    }
    
    // 構(gòu)造函數(shù), ES5中并不支持構(gòu)造函數(shù), 我們只能自己模擬構(gòu)造函數(shù)
    var init = function() {
        // 構(gòu)造函數(shù)代碼
        this.x_public = 11;
        this.y_public = 22;
    }
    init(); // 直接執(zhí)行
}

// 創(chuàng)建定義好的Shape類(lèi)對(duì)象
var shape = new Shape();
console.log(shape.x); // 會(huì)報(bào)錯(cuò), 因?yàn)槭褂胿ar來(lái)定義的屬性是私有屬性
console.log(shape.x_publick); // 訪(fǎng)問(wèn)對(duì)象的公有屬性
shape.show_public(); // 調(diào)用對(duì)象的公有方法


// 帶參數(shù)的構(gòu)造函數(shù)
function Shape2(ax, ay) {
    this.x_public = 1;
    this.y_public = 2;
    // 模擬構(gòu)造函數(shù), 把參數(shù)傳入
    var init = function() {
        this.x_public = ax;
        this.y_public = ay;
    }
    init();
}
// 創(chuàng)建帶參數(shù)的Shape2類(lèi)對(duì)象
var shape2 = new Shape(20, 30);
console.log(shape2.x_public);

1.2.6.2 ES6中類(lèi)的定義

// 在ES6中新增了Class關(guān)鍵字可以用來(lái)定義類(lèi)
Class Shape {
    // 同樣, 私有屬性/方法 用var定義; 公有屬性/方法用 this.定義
    this.x_public = 1;
    this.y_public = 2;
    // ES6中提供了constructor專(zhuān)門(mén)用來(lái)定義構(gòu)造函數(shù)
    constructor(ax, ay) {
        this.x_public = ax;
        this.y_public = ay;
    }
    // ES6中靜態(tài)屬性的定義同ES5, 對(duì)于靜態(tài)方法有static關(guān)鍵字來(lái)定義
    Shape.z = 3;
    static show_static(){
        console.log("我是靜態(tài)方法");
    }
}

1.2.7 map和reduce

數(shù)組中新增了map和reduce方法

  1. map方法:接收一個(gè)函數(shù),將原數(shù)組中的所有元素用這個(gè)函數(shù)處理放入新數(shù)組返回
let arr = ['1', '20', '-5', '3']
arr = arr.map(item => item*2);
console.log(arr);  // [2, 40, -10, 6]
  1. reduce方法:為數(shù)組中的每一個(gè)元素依次執(zhí)行回調(diào)函數(shù),不包括數(shù)組中被刪除或從未被賦值的元素
let arr = [2, 40, -10, 6]
// arr.reduce(callback, [initialValue])
/**
* callback回調(diào)函數(shù)中可以傳4個(gè)參數(shù):
* 1. previousValue(上一次調(diào)用回調(diào)函數(shù)返回的值, 或者是提供的初始值initialValue, 如果沒(méi)有指定初始值就默認(rèn)從數(shù)組中第一個(gè)元素開(kāi)始)
* 2. currentValue(數(shù)組中當(dāng)前正在被處理的元素)
* 3. index(當(dāng)前元素在數(shù)組中的索引, 沒(méi)有就默認(rèn)從數(shù)組中的第二個(gè)元素開(kāi)始)
* 4. array(調(diào)用reduce的數(shù)組, 沒(méi)有就默認(rèn)為當(dāng)前數(shù)組)
*/
let result = arr.reduce((a, b) => {
    console.log("上一次處理后的元素:" + a);
    console.log("當(dāng)前正在處理的元素:" + b);
    return a + b;
});
console.log("最終結(jié)果為:" + result);

// 打印結(jié)果為:
/*
    上一次處理后的元素:2
    當(dāng)前正在處理的元素:40
    上一次處理后的元素:42
    當(dāng)前正在處理的元素:-10
    上一次處理后的元素:32
    當(dāng)前正在處理的元素:6
    最終結(jié)果為:38
*/

1.2.8 Promise

在 JavaScript 的世界中,所有代碼都是單線(xiàn)程執(zhí)行的。由于這個(gè)“缺陷”,導(dǎo)致 JavaScript 的所有網(wǎng)絡(luò)操作,瀏覽器事件,都必須是異步執(zhí)行。異步執(zhí)行可以用回調(diào)函數(shù)實(shí)現(xiàn)。一旦有一連串的 ajax 請(qǐng)求 a,b,c,d,... 后面的請(qǐng)求依賴(lài)前面的請(qǐng)求結(jié)果,就需要層層嵌套。這種縮進(jìn)和層層嵌套的方式,非常容易造成上下文代碼混亂,我們不得不非常小心翼翼處理內(nèi)層函數(shù)與外層函數(shù)的數(shù)據(jù),一旦內(nèi)層函數(shù)使用了上層函數(shù)的變量,這種混亂程度就會(huì)加劇......總之,這種“層疊上下文”的層層嵌套方式,著實(shí)增加了神經(jīng)的緊張程度。使用Promise可以封裝異步操作。

案例:用戶(hù)登錄,并展示該用戶(hù)的各科成績(jī)。在頁(yè)面發(fā)送兩次請(qǐng)求:

  1. 查詢(xún)用戶(hù),查詢(xún)成功說(shuō)明可以登錄
  2. 查詢(xún)用戶(hù)成功,查詢(xún)科目
  3. 根據(jù)科目查詢(xún)結(jié)果,獲取成績(jī)

分析:此時(shí)后臺(tái)應(yīng)該提供三個(gè)接口,一個(gè)提供用戶(hù)查詢(xún)接口,一個(gè)提供科目接口,一個(gè)提供各科成績(jī)接口,為了渲染方便,最好響應(yīng) json 數(shù)據(jù)。

// 以前的嵌套寫(xiě)法:
// 第1層: 查詢(xún)當(dāng)前用戶(hù)信息
// 第2層: 按照當(dāng)前用戶(hù)的id查詢(xún)出用戶(hù)的課程
// 第3層: 按照當(dāng)前課程id查詢(xún)出課程的分?jǐn)?shù)
$.ajax({
    url:"mock/user.json",  // 準(zhǔn)備json文件, 模擬后臺(tái)響應(yīng)回來(lái)的數(shù)據(jù)
    success(data) {
        console.log("查詢(xún)到的用戶(hù):", data);
        $.ajax({
            url: `mock/user_corse_${data.id}.json`,
            success(data) {
                console.log("查詢(xún)到的課程:", data);
                $.ajax({
                    url: `mock/corse_score_${data.id}.json`,
                    success(data){
                        console.log("查詢(xún)到的分?jǐn)?shù):", data)
                    },
                    error(error){...}
                });
            },
            error(error){...}
        });
    },
    error(error){...}
});
    
// 使用Promise封裝異步操作
// Promise對(duì)象創(chuàng)建的時(shí)候可以傳入一個(gè)函數(shù)
// resolve方法: 請(qǐng)求成功后把數(shù)據(jù)接著往下傳
// reject方法: 請(qǐng)求失敗后把異常往下傳
let p = new Promise((resolve, reject) => {
    $.ajax({
        url: "mock/user.json",
        success: function(data) {
            console.log("查詢(xún)到的用戶(hù):", data);
            resolve(data);  // 把data往下傳
        },  
        error: function(err) {
            reject(err);  // 把err往下傳
        }
    });
});

// then可以接收resolve方法中傳下來(lái)的data, 然后寫(xiě)上一步請(qǐng)求成功后接著做的事
// catch可以接收reject方法中傳下來(lái)的err, 然后寫(xiě)上一步請(qǐng)求失敗后接著做的事
p.then((obj) => {  // 這里的obj就是上一步傳下來(lái)的data
    console.log("上一步的結(jié)果為:", obj);
    return new Promise((resolve, reject) => {  // 因?yàn)槲覀冞€要把data接著往下傳, 所以這里的then需要返回一個(gè)Promise
        $.ajax({
            url: `mock/user_corse_${obj.id}.json`,
            success: function(data) {  // 這里的data是請(qǐng)求成功得到的數(shù)據(jù)
                console.log("查詢(xún)到的課程:", data);
                resolve(data);  // 把data往下傳
            },  
            error: function(err) {
                reject(err);
            }
        })
    })
}).then((obj) => {  // 上一步返回的是Promise所以在這還可以接著.then做下一步的事
    console.log("上一步的結(jié)果為:", obj);
    $.ajax({
        url: `mock/corse_score_${obj.id}.json`,
        success: function(data) {
            console.log("查詢(xún)到的分?jǐn)?shù):", data);
        },  
        error: function(err) {
        }
    })
}).catch((err) => {
    // 失敗后做的事
})
// 進(jìn)一步簡(jiǎn)化: 將返回Promise封裝為一個(gè)方法
function get(url) {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: url,
            success: function(data) {  // 請(qǐng)求成功后返回的數(shù)據(jù)
                resolve(data);  // 把data往下傳
            },  
            error: function(err) {
                reject(err);  // 把err往下傳
            }
        });
    })
}

get("mock/user.json")
    .then((obj) => {
        console.log("查詢(xún)到的用戶(hù):", obj);
        return get(`mock/user_corse_${obj.id}.json`);
    })
    .then((obj) => {
        console.log("查詢(xún)到的課程:", obj);
        return get(`mock/corse_score_${obj.id}.json`);
    })
    .then((obj) => {
        console.log("查詢(xún)到的課程:", obj);
    })
    .catch((err) => {
    console.log("出現(xiàn)異常!");
    });

1.2.9 模塊化

模塊化就是把代碼進(jìn)行拆分,方便重復(fù)利用,類(lèi)似 Java 中的導(dǎo)包:要使用一個(gè)包必須先導(dǎo)包。而 JS 中沒(méi)有包的概念,換來(lái)的是模塊

1.2.9.1 模塊化雛形

aaa.js:負(fù)責(zé)導(dǎo)出

// 匿名函數(shù)的寫(xiě)法, 閉包的方式, 可以防止命名沖突, 因?yàn)橐粋€(gè)函數(shù)就是一個(gè)單獨(dú)的作用域
var modelA = (function() { 
    var obj = {}; // 定義一個(gè)對(duì)象
    
    var name = "小明";
    var age = 22;
    
    function sum(num1, num2) {
        return num1 + num2;
    }
    // 給對(duì)象賦值
    obj.name = name;
    obj.age = age;
    obj.sum = sum;
    // 將這個(gè)對(duì)象作為當(dāng)前匿名函數(shù)的返回值, 返回給modelA
    return obj;
})() // 后面加一個(gè)小括號(hào)表示立即執(zhí)行

bbb.js:負(fù)責(zé)導(dǎo)入

;(function() {
    // 可以直接使用導(dǎo)出的對(duì)象
    console.log(modelA.name);
    console.log(modelA.age);
    console.log(modelA.sun(10, 20));
})()

index.html:引入兩個(gè)js文件看效果

<body>
    <h1>hello world</h1>
    <script src="./aaa.js"></script>
    <script src="./bbb.js"></script>
</body>

1.2.9.2 CommonJS規(guī)范的模塊化

webpack模塊化的實(shí)現(xiàn)方式,需要nodejs作為底層支撐,不然無(wú)法解析

aaa.js:負(fù)責(zé)導(dǎo)出

// 不再需要匿名函數(shù), 因?yàn)樵谀K化規(guī)范里, 一個(gè)js文件就是一個(gè)模塊  
var name = "小明";
var age = 22;

function sum(num1, num2) {
    return num1 + num2;
}

// 導(dǎo)出, 要導(dǎo)出什么數(shù)據(jù)就把數(shù)據(jù)放到module.exports的對(duì)象中
// (當(dāng)對(duì)象key和value同名時(shí)也可以用ES6的增強(qiáng)寫(xiě)法)
module.exports = {
    name: name,
    age: age,
    sum: sum
}

bbb.js:負(fù)責(zé)導(dǎo)入

// 因?yàn)樵谀K化規(guī)范里, 一個(gè)js文件就是一個(gè)模塊,
// 直接用關(guān)鍵字require("js文件路徑")就可以獲取到導(dǎo)出的模塊對(duì)象
var moduleA = require("./aaa.js");
console.log(moduleA.name);
console.log(moduleA.age);
console.log(moduleA.sum(10, 20));

// 也可以用ES6對(duì)象解構(gòu)的寫(xiě)法
var {name, age, sum} = require("./aaa.js");
console.log(name);
console.log(age);
console.log(sum(10, 20));

index.html:引入兩個(gè)js文件看效果

<body>
    <h1>hello world</h1>
    <script src="./aaa.js"></script>
    <script src="./bbb.js"></script>
</body>

1.2.9.3 ES6規(guī)范的模塊化(常用)

模塊功能主要由兩個(gè)命令構(gòu)成:export 和 mport

  • export 命令用于規(guī)定模塊的對(duì)外接口(export不僅能導(dǎo)出對(duì)象,一切 JS 變量都可以導(dǎo)出。比如基本類(lèi)型變量、函數(shù)、數(shù)組、對(duì)象)
  • import 命令用于導(dǎo)入其他模塊提供的功能

aaa.js:負(fù)責(zé)導(dǎo)出

導(dǎo)出方式一:先定義,再導(dǎo)出

var name = "小明";
var age = 22;

export {
    name, age
}

導(dǎo)出方式二:邊定義邊導(dǎo)出

export var name = "小明";
export var age = 22;

函數(shù)和類(lèi)的導(dǎo)出:

// 函數(shù)的先定義后導(dǎo)出
function sum(num1, num2) {
    return num1 + num2;
}
export {
    sum
}

// 函數(shù)的邊定義邊導(dǎo)出
export function sum(num1, num2) {
    return num1 + num2;
}

// 類(lèi)的先定義后導(dǎo)出
function Person() { // ES5類(lèi)的寫(xiě)法
    var name = "";
    var age = 0;
    // ES5定義類(lèi)的構(gòu)造器
    (function (name, age) {
        this.name = name;
        this.age = age;
    })()
    // 定義類(lèi)的方法
    run() {
        console.log(this.name + "在奔跑");
    }
}

class Person { // ES6類(lèi)的寫(xiě)法
    name = "";
    age = 0;
    // ES6直接用constructor關(guān)鍵字就可以定義類(lèi)的構(gòu)造器
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    // 定義類(lèi)的方法
    run() {
        console.log(this.name + "在奔跑");
    }
}

默認(rèn)導(dǎo)出:導(dǎo)入者在使用默認(rèn)導(dǎo)出時(shí)可以自行給數(shù)據(jù)命名。注意:一個(gè)js文件只能有一個(gè)默認(rèn)導(dǎo)出

// 先定義后導(dǎo)出的默認(rèn)導(dǎo)出
function sum(num1, num2) {
    return num1 + num2;
}
export default sum;

// 邊定義邊導(dǎo)出的默認(rèn)導(dǎo)出
export default function(num1, num2) {
    return num1 + num2;
}

bbb.js:負(fù)責(zé)導(dǎo)入

// 導(dǎo)入數(shù)據(jù)
import {name, age} from ",/aaa.js";
console.(name);
console.(age);

// 導(dǎo)入函數(shù)/類(lèi)
import {sum, Person} from ",/aaa.js";
console.(sum(10, 20));
var p = new Person("小明", 18); // 根據(jù)導(dǎo)入的對(duì)象類(lèi)型創(chuàng)建對(duì)象
console.log(p.run()); // 使用對(duì)象的方法

// 導(dǎo)入export default的內(nèi)容
import moduleA from "./aaa.js"; // 這里的moduleA是在導(dǎo)入時(shí)自定義的名
console.log(moduleA(10, 30)); // modelA的類(lèi)型是根據(jù)導(dǎo)出時(shí)的類(lèi)型來(lái)決定的, 這里的moduleA就是一個(gè)函數(shù)

// 統(tǒng)一全部導(dǎo)入
import * as moduleA_All from "./aaa.js"; // 用通配符把所有導(dǎo)出的內(nèi)容放入一個(gè)moduleA_All的對(duì)象里
console.(moduleA_All.name);
console.(moduleA_All.age);
console.(moduleA_All.sum(19, 38));

index.html:引入兩個(gè)js文件看效果

<body>
    <h1>hello world</h1>
    <!-- 引入js文件, 并指定類(lèi)型為模塊 -->
    <script src="./aaa.js" type="module"></script>
    <script src="./bbb.js" type="module"></script>
</body>

二、Vue

2.1 MVVM思想

  • M:即 Model,模型,包括數(shù)據(jù)和一些基本操作
  • V:即 View,視圖,頁(yè)面渲染效果
  • VM:即 View-Model,模型與視圖間的雙向操作(無(wú)需開(kāi)發(fā)人員干涉)

在 MVVM 之前,開(kāi)發(fā)人員需要從后端獲取需要的數(shù)據(jù)模型,然后要通過(guò) DOM 操作 Model 渲染到 View 中。而后當(dāng)用戶(hù)操作視圖,我們還需要通過(guò) DOM 獲取 View中的數(shù)據(jù),然后同步到 Model中。

而在 MVVM 中,VM 要做的事就是把 DOM 操作完全封裝起來(lái),開(kāi)發(fā)人員不用再關(guān)心 Model 和 View 之間是如何相互影響的。

  • 只要我們 Model 發(fā)生了改變,View 上自然就會(huì)表現(xiàn)出來(lái)
  • 當(dāng)用戶(hù)修改了 View,Model 中的數(shù)據(jù)也會(huì)跟著改變

把開(kāi)發(fā)人員從繁瑣的 DOM 操作中解放出來(lái),把關(guān)注點(diǎn)放在如何操作 Model 上。

2.2 Vue簡(jiǎn)介

Vue是一套用于構(gòu)建用戶(hù)界面的漸進(jìn)式框架。與其他大型框架不同的是,Vue被設(shè)計(jì)為可以自底向上逐層應(yīng)用。Vue的核心庫(kù)只關(guān)注視圖層,不僅易于上手,還便于與第三方庫(kù)或既有項(xiàng)目整合。另一方面,當(dāng)與現(xiàn)代化的工具鏈以及各種支持類(lèi)庫(kù)結(jié)合使用時(shí),Vue也完全能夠?yàn)閺?fù)雜的單頁(yè)應(yīng)用提供驅(qū)動(dòng)。

官網(wǎng):http://cn.vuejs.org/

教程參考:http://cn.vuejs.org/v2/guide/

Git地址:http://github.com/vuejs

2.3 入門(mén)案例

2.3.1 安裝

Step1:創(chuàng)建一個(gè)文件夾作為Vue項(xiàng)目,用VSCode打開(kāi)

Step2:在終端用npm初始化項(xiàng)目,表示該項(xiàng)目是由npm來(lái)管理

npm init -y

Step3:用npm安裝Vue

npm install vue

Step4:創(chuàng)建一個(gè)html文件,在<script>標(biāo)簽中引入vue.js即可使用Vue

<!-- 引入vue.js即可使用vue -->
<script src="./node_modules/vue/dist/vue.js"></script> 

2.3.2 Vue聲明式渲染

<body>
    
    <div id="div1">
        <!-- {{name}}表示動(dòng)態(tài)的從Vue的數(shù)據(jù)區(qū)中取出名為name屬性的值 -->
        <h1>{{name}}, 非常帥</h1>  
    </div>
    <!-- 引入vue.js即可使用vue -->
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",  // Vue對(duì)象管控的元素的id
            data: {  // 存放當(dāng)前Vue實(shí)例的所有數(shù)據(jù)
                name: "張三"
            }
        });
        vm.name = "李四";  // 改動(dòng)后頁(yè)面會(huì)實(shí)時(shí)跟著改變(即Model發(fā)生了改變, View也跟著改變)
    </script>
</body>

2.3.3 雙向綁定

Model變化,View隨之變化,反之亦然。

<body>
    
    <div id="div1">
        <!-- v-model="num"表示這個(gè)輸入框中的值和Vue的數(shù)據(jù)區(qū)中的num屬性進(jìn)行綁定(雙向綁定)
            在輸入框中輸入數(shù)據(jù)Vue的數(shù)據(jù)區(qū)中的num屬性的值也會(huì)隨之改變(即View改變, Model隨之改變) -->
        <input type="text" v-model="num">
        <h1>{{name}}, 非常帥, 有{{num}}個(gè)人為他點(diǎn)贊!</h1>  
    </div>

    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",  // Vue對(duì)象管控的元素的id
            data: {
                name: "張三",
                num: 0
            }
        });
    </script>
</body>

2.3.4 事件處理

<body>
    
    <div id="div1">
        <input type="text" v-model="num">
        <!-- v-on用來(lái)綁定事件, click表示單擊事件, ="表示單擊后想要做什么"(當(dāng)操作復(fù)雜時(shí)也可以用方法)
            每點(diǎn)擊一次botton按鈕, Vue數(shù)據(jù)區(qū)中的就會(huì)num++, 而input輸入框與num綁定,
            所以輸入框中的數(shù)值也會(huì)跟著改變, h1中的{{num}}是從Vue的數(shù)據(jù)區(qū)中取值,
            所以h1中顯示的數(shù)據(jù)也會(huì)跟著改變 -->
        <botton v-on:click="num++">點(diǎn)贊</botton>
        <!-- 單擊事件綁定Vue中的cancel方法 -->
        <botton v-on:click="cancel">取消點(diǎn)贊</botton>
        <h1>{{name}}, 非常帥, 有{{num}}個(gè)人為他點(diǎn)贊!</h1>  
    </div>

    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",  // Vue對(duì)象管控的元素的id
            data: {
                name: "張三",
                num: 0
            },
            methods: {  // 用來(lái)存放當(dāng)前Vue實(shí)例的所有方法
                cancel() {
                    this.num--;
                }
            }
        });
    </script>
</body>

2.4 Vue指令

2.4.1 插值表達(dá)式

2.4.1.1 雙花括號(hào)

格式:{{表達(dá)式}}

  • 表達(dá)式支持JS語(yǔ)法,可以調(diào)用JS內(nèi)置函數(shù)(必須有返回值)
  • 表達(dá)式必須有返回結(jié)果,例如:1 + 1。沒(méi)有返回結(jié)果的表達(dá)式不允許使用,例如:let a = 1 + 1
  • 可以直接取Vue實(shí)例中定義的數(shù)據(jù)或函數(shù)

注意:花括號(hào)只能完全顯示出屬性的值,如果屬性是一個(gè)html樣式的字符串,不會(huì)轉(zhuǎn)變?yōu)闃邮?。比如name = "<h1>hello</h1>",則在頁(yè)面上會(huì)顯示<h1>hello</h1>而不會(huì)顯示樣式。

2.4.1.2 插值閃爍

使用花括號(hào)當(dāng)網(wǎng)速慢時(shí)(在瀏覽器的開(kāi)發(fā)者頁(yè)面中的Network改為Slow 3G可以模擬網(wǎng)速慢的效果),會(huì)直接把{{name}}這個(gè)插值表達(dá)式給顯示在頁(yè)面上,即頁(yè)面沒(méi)有完全渲染之前把插值表達(dá)式顯示出來(lái),這就是插值閃爍。可以使用v-text指令來(lái)解決插值閃爍問(wèn)題,v-text會(huì)等Vue屬性的內(nèi)容完全渲染后再顯示到頁(yè)面上,而不會(huì)把插值表達(dá)式顯示出來(lái)。

2.4.1.3 v-text 和 v-html

使用v-text可以解決花括號(hào)的插值閃爍的問(wèn)題,使用v-html可以解決花括號(hào)html樣式不顯示的問(wèn)題。

<body>
    <div id="div1">
        <!-- 花括號(hào)方式取值 顯示:<h1>hello</h1> (會(huì)有插值閃爍, 不帶<h1>樣式) -->
        {{name}} {{1 + 1}} {{hello()}}<br/>
        <!-- 使用v-text取值 顯示:<h1>hello</h1> (沒(méi)有插值閃爍, 不帶<h1>樣式) -->
        <span v-text="name"></span>
        <!-- 使用v-html取值 顯示:hello (帶<h1>樣式)-->
        <span v-html="name"></span>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el:"#div1",
            data: {
                name: "<h1>hello</h1>"
            },
            methods: {
                hello() {
                    return "world";
                }
            }
        })
    </script>
</body>

2.4.2 v-bind

插值表達(dá)式是給標(biāo)簽體內(nèi)綁定值的,如果想在html標(biāo)簽的屬性中取到Vue實(shí)例的數(shù)據(jù),可以用v-bind指令

可以簡(jiǎn)寫(xiě)為 :屬性名

<body>
    <div id="div1">
        <!-- 使用v-bind:標(biāo)簽中的屬性名="Vue中的數(shù)據(jù)名" -->
        <a v-bind:href="link"></a>
        <!-- v-bind對(duì)于class和style屬性有特殊效果
            可以通過(guò)v-bind:class="{active: 綁定Vue實(shí)例中的一個(gè)boolean類(lèi)型的屬性}"
            (這里花括號(hào)的意思是把a(bǔ)ctive和'text-danger'封裝成一個(gè)對(duì)象)
            來(lái)決定class屬性中是否有active、text-danger等值
            注意: 因?yàn)閠ext-danger中有特殊字符-, 所以要用單引號(hào)包裹起來(lái) -->
        <span v-bind:class="{active: isActive, 'text-danger': hasError}">你好</span>
        <span :style="{color: colorVue, 'font-size': sizeVue}">你好1</span>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                link: "http://www.baidu.com",
                isActive: true,
                hasError: true,
                colorVue: 'red',
                sizeVue: '36px'
            }
        })
    </script>
</body>

注意: 插值表達(dá)式和v-bind都的單向綁定,即數(shù)據(jù)變化會(huì)引起頁(yè)面的變化,但是頁(yè)面的變化不會(huì)改變數(shù)據(jù)。(比如直接在頁(yè)面上修改color: blue把顏色改為blue,頁(yè)面的顏色會(huì)變,但是Vue中的數(shù)據(jù)不會(huì)改變)

2.4.3 v-model

一般用于表單項(xiàng)和自定義組件

<body>
    <div id="div1">
        精通的語(yǔ)言: <br/>
        <!-- 使用v-model綁定Vue數(shù)據(jù)中的language屬性
            這三個(gè)input都屬于同一組多選框, 綁定同一個(gè)language屬性即可
            它們選定的value會(huì)被保存在Vue中的language數(shù)組中 -->
        <input type="checkbox" v-model="language" value="Java">Java<br/>
        <input type="checkbox" v-model="language" value="PHP">PHP<br/>
        <input type="checkbox" v-model="language" value="Python">Python<br/>
        <!-- 數(shù)組的join方法, 可以將數(shù)組連接成字符串顯示 -->
        選中了:{{language.join(",")}}
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                language: []
            }
        })
    </script>
</body>

2.4.4 v-on

2.4.4.1 基本用法

用來(lái)綁定事件

可以簡(jiǎn)寫(xiě)為 @事件名

<body>
    <div id="div1">
        <!-- 事件中直接寫(xiě)JS片段 -->
        <botton v-on:click="num++">點(diǎn)贊</botton>
        <!-- 事件指定一個(gè)回調(diào)函數(shù), 必須是Vue實(shí)例中定義的函數(shù) -->
        <botton @click="cancle">取消點(diǎn)贊</botton>
        <br/>
        獲得了{(lán){num}}個(gè)贊
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                num: 0
            },
            methods: {
                cancle(){
                    this.num--;
                }
            }
        })
    </script>
</body>

2.4.4.2 事件修飾符

在事件處理程序中調(diào)用 event.preventDefault() 或 event.stopPropagation() 是非常常見(jiàn)的需求。盡管我們可以在方法中輕松實(shí)現(xiàn)這點(diǎn),但更好的方式是:方法只有純粹的數(shù)據(jù)邏輯,而不是去處理DOM事件細(xì)節(jié)。

為了解決這個(gè)問(wèn)題,Vue為 v-on 提供了事件修飾符。修飾符是由點(diǎn)開(kāi)頭的指令后綴來(lái)表示的:

  • .stop:阻止事件冒泡到父元素
  • .prevent:阻止默認(rèn)事件發(fā)生
  • .capture:使用事件捕獲模式
  • .self:只有元素自身觸發(fā)事件才執(zhí)行(冒泡或捕獲的都不執(zhí)行)
  • .once:只執(zhí)行一次
<body>
    <div id="div1">

        <!-- 事件修飾符 -->
        
        <!-- 這里有一個(gè)事件冒泡, 即點(diǎn)了小div就相當(dāng)于點(diǎn)了大div, 所以我們會(huì)看到兩次彈窗(事件冒泡到父元素)
            可以將小div的事件加上.stop即可阻止事件冒泡到父元素 -->
        <!-- 我們想要大div只能被點(diǎn)擊1次, 可以給事件加上.once -->
        <div style="border: 1px solid red; padding: 20px" v-on:click.once="hello">
            大div
            <div style="border: 1px solid blue; padding: 20px" @click.stop="hello">
                小div<br/>
                <!-- 小div彈窗完了之后會(huì)有一個(gè)跳轉(zhuǎn)頁(yè)面的默認(rèn)事件,
                    這里我們可以用.prevent來(lái)阻止這個(gè)默認(rèn)事件,
                    這樣我們就只會(huì)看到彈窗而不會(huì)看到跳轉(zhuǎn)頁(yè)面 -->
                <a href="http//www.baidu.com" @click.prevent>去百度</a>
                <!-- 使用prevent阻止默認(rèn)事件后我們還可以給它指定一個(gè)回調(diào)函數(shù) -->
                <a href="http//www.baidu.com" @click.prevent="hello">去百度</a>
                <!-- 還可以把prevent和stop連用, 既阻止了默認(rèn)事件, 又阻止了事件冒泡 -->
                <a href="http//www.baidu.com" @click.prevent.stop="hello">去百度</a>
            </div>
        </div>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                num: 0
            },
            methods: {
                cancle(){
                    this.num--;
                },
                hello(){
                    alert("點(diǎn)擊了");
                }
            }
        })
    </script>
</body>

2.4.4.3 按鍵修飾符

在監(jiān)聽(tīng)鍵盤(pán)事件時(shí),我們經(jīng)常需要檢查常見(jiàn)的鍵值(KeyCode)。Vue允許為 v-on 在監(jiān)聽(tīng)鍵盤(pán)事件的時(shí)候添加按鍵修飾符,即一些常用的按鍵,讓我們不用去記憶常用按鍵的KeyCode:

  • .enter
  • .tab
  • .delete:可以捕獲刪除和退格鍵
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
<body>
    <div id="div1">

        <!-- 按鍵修飾符 -->
        
        <!-- 給這個(gè)輸入框綁定監(jiān)聽(tīng)鍵盤(pán)事件, 按鍵盤(pán)up鍵表示num+2, 按鍵盤(pán)down鍵表示num-2 -->
        <input type="text" v-model:"num" @keyup.up="num+=2" @keyup.down="num-=2"><br/>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                num: 0
            }
        })
    </script>
</body>

2.4.4.4 組合按鈕

可以用如下修飾符來(lái)實(shí)現(xiàn)僅在按下相應(yīng)按鍵時(shí)才出發(fā)鼠標(biāo)或鍵盤(pán)事件的監(jiān)聽(tīng)器:

  • .ctrl
  • .alt
  • .shift
<body>
    <div id="div1">

        <!-- 組合按鈕 -->
        
        <!-- 此時(shí)按住ctrl加鼠標(biāo)單擊才能觸發(fā)事件 -->
        <input type="text" v-model:"num" @click.ctrl="num=10"><br/>
        <!-- 此時(shí)按住alt加鍵盤(pán)up鍵才能觸發(fā)事件 -->
        <input type="text" v-model:"num" @keyup.alt.up="num=20"><br/>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                num: 0
            }
        })
    </script>
</body>

2.4.5 v-for

2.4.5.1 遍歷數(shù)組

<body>
    <div id="div1">
        <ul>
            <!-- 當(dāng)前元素 in 待遍歷的數(shù)組 (類(lèi)似Python的 for xx in xxs) -->
            <li v-for="user in users">
                {{user.name}}==>{{user.gender}}==>{{user.age}}
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {name: '柳巖', gender: '女', age: 21},
                    {name: '張三', gender: '男', age: 23},
                    {name: '楊冪', gender: '女', age: 25},
                    {name: '古力娜扎', gender: '女', age: 22},
                    {name: '李四', gender: '男', age: 18},
                ]
            }
        })
    </script>
</body>

2.4.5.2 數(shù)組索引

<body>
    <div id="div1">
        <ul>
            <!-- (當(dāng)前元素, 當(dāng)前元素在數(shù)組中的索引) in 待遍歷的數(shù)組 -->
            <li v-for="(user, index) in users">
                當(dāng)前索引:{{index}}==>{{user.name}}==>{{user.gender}}==>{{user.age}}
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {name: '柳巖', gender: '女', age: 21},
                    {name: '張三', gender: '男', age: 23},
                    {name: '楊冪', gender: '女', age: 25},
                    {name: '古力娜扎', gender: '女', age: 22},
                    {name: '李四', gender: '男', age: 18},
                ]
            }
        })
    </script>
</body>

2.4.5.3 遍歷對(duì)象

<body>
    <div id="div1">
        <ul>
            
            <li v-for="user in users">
                <!-- 遍歷對(duì)象可以依次取出對(duì)象中的各個(gè)屬性 -->
                <!-- 當(dāng)前屬性的值 in 待遍歷的對(duì)象 -->
                對(duì)象信息:
                <span v-for="value in user">{{value}}==></span>
                <!-- (當(dāng)前屬性的值, 當(dāng)前屬性的鍵) in 待遍歷的對(duì)象 -->
                對(duì)象信息:
                <span v-for="(value, key) in user">{{key}}:{{value}}==></span>
                <!-- (當(dāng)前屬性的值, 當(dāng)前屬性的鍵, 當(dāng)前屬性的索引) in 待遍歷的對(duì)象 -->
                對(duì)象信息:
                <span v-for="(value, key, index) in user">{{index}}:{{key}}:{{value}}</span>
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {name: '柳巖', gender: '女', age: 21},
                    {name: '張三', gender: '男', age: 23},
                    {name: '楊冪', gender: '女', age: 25},
                    {name: '古力娜扎', gender: '女', age: 22},
                    {name: '李四', gender: '男', age: 18},
                ]
            }
        })
    </script>
</body>

2.4.5.4 key屬性

在使用v-for遍歷時(shí)最好都帶上一個(gè)key屬性來(lái)區(qū)分不同的數(shù)據(jù),提高Vue的渲染效率

<body>
    <div id="div1">
        <ul>
            <!-- 數(shù)組中的每個(gè)對(duì)象的id都不同, 所以 :key="對(duì)象中的id屬性"
                :key其實(shí)是 v-bind:key的縮寫(xiě) -->
            <li v-for="user in users" :key="user.id">
                {{user.name}}==>{{user.gender}}==>{{user.age}}
                <!-- 當(dāng)遍歷的是常規(guī)數(shù)組時(shí), key可以="index" -->
                <span v-for="(value, index) in nums" :key="index"></span>
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {id:1, name: '柳巖', gender: '女', age: 21},
                    {id:2, name: '張三', gender: '男', age: 23},
                    {id:3, name: '楊冪', gender: '女', age: 25},
                    {id:4, name: '古力娜扎', gender: '女', age: 22},
                    {id:5, name: '李四', gender: '男', age: 18},
                ],
                nums: [1,2,2,3,3,4,4]
            }
        })
    </script>
</body>

2.4.6 v-if 和 v-show

v-if:條件判斷。當(dāng)判斷的結(jié)果為true時(shí),所在的元素才會(huì)被渲染

v-show:同樣為條件判斷。當(dāng)判斷的結(jié)果為true時(shí),所在的元素才會(huì)被顯示

區(qū)別:v-if 是整個(gè)元素都消失了;v-show 是在元素中加上樣式:style="display: none" 來(lái)隱藏元素,我們?cè)诳刂婆_(tái)手動(dòng)去掉style="display: none" 依然可以顯示,而 v-if 無(wú)法操作

<body>
    <div id="div1">
        <botton @click="isShow = !isShow">點(diǎn)我呀</botton>
        <h1 v-if="isShow">v-if 顯示</h1>
        <h1 v-show="isShow">v-show 顯示</h1>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                isShow: true
            }
        })
    </script>
</body>

2.4.7 v-else 和 v-else-if

類(lèi)似Java中的

if (條件判斷1) {
    ...
} else if (條件判斷2) {
    ...
} else {
    ...
}
<body>
    <div id="div1">
        <botton @click="num = Math.random()">點(diǎn)我呀</botton>
        <span>{{num}}</span>
        <h1 v-if="num >= 0.75">  <!-- &gt;是在頁(yè)面中顯示大于號(hào) > -->
            看到我啦?  &gt;= 0.75
        </h1>
        <h1 v-else-if="num >= 0.5">
            看到我啦?  &gt;= 0.5
        </h1>
        <h1 v-else-if="num >= 0.2">
            看到我啦?  &gt;= 0.2
        </h1>
        <h1 v-else>  <!-- &lt;是在頁(yè)面中顯示小于號(hào) < -->
            看到我啦?  &lt; 0.2
        </h1>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                num: 1
            }
        })
    </script>
</body>

2.4.8 v-for 和 v-if 組合使用

<body>
    <div id="div1">
        <ul>
            <!-- 給v-for的循環(huán)加上條件才顯示 -->
            <li v-for="user in users" :key="user.id" v-if"user.gender == '女'">
                {{user.name}}==>{{user.gender}}==>{{user.age}}
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {id: 1, name: '柳巖', gender: '女', age: 21},
                    {id: 2, name: '張三', gender: '男', age: 23},
                    {id: 3, name: '楊冪', gender: '女', age: 25},
                    {id: 4, name: '古力娜扎', gender: '女', age: 22},
                    {id: 5, name: '李四', gender: '男', age: 18},
                ]
            }
        })
    </script>
</body>

2.5 計(jì)算屬性和偵聽(tīng)器

2.5.1 計(jì)算屬性

某些結(jié)果是基于數(shù)據(jù)實(shí)時(shí)計(jì)算出來(lái)的,我們可以利用計(jì)算屬性來(lái)完成渲染

<body>
    <div id="div1">
        <ul>
            <!-- 這里是總價(jià)需要用到西游記的價(jià)格*數(shù)量 + 水滸傳的價(jià)格*數(shù)量來(lái)實(shí)時(shí)計(jì)算 -->
            <li>西游記,價(jià)格:{{xyjPrice}},數(shù)量:<input type="number" v-model="xyjNum"></li>
            <li>水滸傳,價(jià)格:{{shzPrice}},數(shù)量:<input type="number" v-model="shzNum"></li>
            <li>總價(jià):{{totalPrice}}</li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                xyjPrice: 91.12,
                shzPrice: 88.00
                xyjNum: 1,
                shzNum: 1
            },
            // computed專(zhuān)門(mén)用來(lái)存放計(jì)算屬性
            computed: {
                totalPrice() {  // 這里將totalPrice聲明為一個(gè)方法
                    return this.xyjPrice*this.xyjNum + this.shzPrice*this.shzNum;
                }
            }
        })
    </script>
</body>

2.5.2 偵聽(tīng)器

偵聽(tīng)器 watch 可以讓我們監(jiān)控一個(gè)值的變化,從而做出相應(yīng)的操作

<body>
    <div id="div1">
        <ul>
            <!-- 這里是總價(jià)需要用到西游記的價(jià)格*數(shù)量 + 水滸傳的價(jià)格*數(shù)量來(lái)實(shí)時(shí)計(jì)算 -->
            <li>西游記,價(jià)格:{{xyjPrice}},數(shù)量:<input type="number" v-model="xyjNum"></li>
            <li>水滸傳,價(jià)格:{{shzPrice}},數(shù)量:<input type="number" v-model="shzNum"></li>
            <li>總價(jià):{{totalPrice}}</li>
            {{msg}}
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                xyjPrice: 91.12,
                shzPrice: 88.00
                xyjNum: 1,
                shzNum: 1,
                msg: ""
            },
            // computed用來(lái)存放當(dāng)前Vue實(shí)例的所有計(jì)算屬性
            computed: {
                totalPrice() {  // 這里將totalPrice聲明為一個(gè)方法
                    return this.xyjPrice*this.xyjNum + this.shzPrice*this.shzNum;
                }
            }
            // watch用來(lái)存放當(dāng)前Vue實(shí)例的所有偵聽(tīng)器
            watch: {
                // 監(jiān)控的屬性(改變后的新值, 以前的舊值)
                xyjNum(newVal, oldVal){
                    // 可以使用彈窗來(lái)查看新值和舊值顯示是否正確:
                    // alert("newVal: " + newVal + "; oldVal: " + oldVal);
                    if (newVal >= 3) {
                        this.msg = "庫(kù)存不足";
                        this.xyjNum = 3;
                    } else {
                        this.msg = "";
                    }
                } 
            }
        })
    </script>
</body>

2.6 過(guò)濾器

過(guò)濾器通常用來(lái)處理文本格式化的操作(比如:數(shù)據(jù)庫(kù)中將性別“男”存為1,性別“女”存為0,我們要根據(jù)數(shù)據(jù)庫(kù)中查出來(lái)的是1還是0來(lái)顯示“男”還是“女”)。

過(guò)濾器可以用在兩個(gè)地方:雙花括號(hào)插值表達(dá)式和 v-bind 表達(dá)式

<body>
    <div id="div1">
        <ul>
            <li v-for="user in users" :key="user.id">
                <!-- 過(guò)濾器的語(yǔ)法:
                    要傳入過(guò)濾器函數(shù)的值 | 過(guò)濾器函數(shù), 
                    然后過(guò)濾器函數(shù)的返回結(jié)果就會(huì)代替?zhèn)魅脒^(guò)濾器函數(shù)的值顯示
                    | 稱(chēng)為管道符 -->
                {{user.id}}==>{{user.name}}==>{{user.gender | genderFilter}}==>{{user.age}}
            </li>
        </ul>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        let vm = new Vue({
            el: "div1",
            data: {
                users: [
                    {id: 1, name: '柳巖', gender: 0, age: 21},
                    {id: 2, name: '張三', gender: 1, age: 23},
                    {id: 3, name: '楊冪', gender: 0, age: 25},
                    {id: 4, name: '古力娜扎', gender: 0, age: 22},
                    {id: 5, name: '李四', gender: 1, age: 18},
                ]
            },
            // filters用來(lái)存放當(dāng)前Vue實(shí)例的所有過(guò)濾器
            // 這里的過(guò)濾器是局部的, 只能在當(dāng)前Vue實(shí)例綁定的元素中使用
            // 過(guò)濾器函數(shù)一定要有返回值
            filters: {
                genderFilter(val) {
                    if (val == 1) {
                        return "男";
                    } else {
                        return "女";
                    }
                }
            }
        })
        
        // 全局過(guò)濾器的寫(xiě)法: 寫(xiě)在Vue實(shí)例的外面, 調(diào)用方法與局部過(guò)濾器相同
        Vue.filter("gFilter", function(val) {
            if (val == 1) {
                return "男~~~";
            } else {
                return "女~~~";
            }
        })
    </script>
</body>

2.7 組件化

在大型應(yīng)用開(kāi)發(fā)的的時(shí)候,頁(yè)面可以劃分為很多部分。往往不同的頁(yè)面也會(huì)有相同的部分,比如可能會(huì)有相同的頭部導(dǎo)航。

但是如果每個(gè)頁(yè)面都獨(dú)自開(kāi)發(fā),這無(wú)疑增加了我們開(kāi)發(fā)的成本。所以我們會(huì)把頁(yè)面中各個(gè)不同的部分拆分成獨(dú)立的組件,然后在不同的頁(yè)面中就可以共享這些組件,避免重復(fù)開(kāi)發(fā)。

在Vue里,所有的Vue實(shí)例都是組件。一個(gè)組件也可以是另一個(gè)組件的子組件。

2.7.1 全局組件

  • 組件其實(shí)也是一個(gè)Vue實(shí)例,因此它在定義時(shí)也會(huì)接收:data、methods、計(jì)算屬性、偵聽(tīng)器等
  • 不同的是,組件不會(huì)與頁(yè)面的元素進(jìn)行綁定,否則就無(wú)法復(fù)用了,因此沒(méi)有 el 屬性
  • 但是組件渲染必須要html模板,所以增加了template屬性,值就是html模板
  • 全局組件定義完畢,任何Vue實(shí)例都可以直接在html中通過(guò)組件名稱(chēng)來(lái)使用組件了
  • data必須是一個(gè)函數(shù),不再是一個(gè)對(duì)象
<body>
    <div id="div1">
        <!-- 非組件化的寫(xiě)法為 -->
        <button @click="count++">我被點(diǎn)擊了{(lán){count}}次</button>
        
        <!-- 聲明為組件后, 直接用組件名作為標(biāo)簽名即可復(fù)用 -->
        <counter></counter>
        <!-- 定義好的組件可以被任意復(fù)用多次, 且數(shù)據(jù)是相互隔離的 -->
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <counter></counter>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        // 非組件化:
        let vm = new Vue({
            el: "#div1"
            data: {
                count: 0
            }
        });
        
        // 全局聲明注冊(cè)一個(gè)組件:
        // 使用Vue的component方法, 傳入兩個(gè)參數(shù): "組件的名稱(chēng)", {對(duì)組件的設(shè)置}
        Vue.component("counter", {
            // 組件的模板
            template: `<button @click="count++">我被點(diǎn)擊了{(lán){count}}次</button>`,
            // 組件的數(shù)據(jù), 寫(xiě)成一個(gè)data方法, 返回值是一個(gè)對(duì)象, 這個(gè)對(duì)象就是以前的data對(duì)象
            data() {
                return {
                    count: 0
                }
            }
        });
    </script>
</body>

2.7.2 局部組件

<body>
    <div id="div1">
        <!-- 局部組件只有注冊(cè)到Vue實(shí)例以后才能在Vue實(shí)例綁定的頁(yè)面元素進(jìn)行使用
            使用方法同樣是用組件名作為標(biāo)簽即可-->
        <counter></counter>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
    
    <script>
        // 局部聲明一個(gè)組件對(duì)象
        const buttonCounter = {
            template: `<button @click="count++">我被點(diǎn)擊了{(lán){count}}次</button>`
            data() {
                return {
                    count: 1
                }
            }
        };
        
        let vm = new Vue({
            el: "#div1",
            // components存放當(dāng)前Vue實(shí)例引用的所有組件
            components: {
                // key: value
                // key是我們自定義的組件名: value是我們聲明的局部組件對(duì)象
                counter: buttonCounter 
            }
        });
    </script>
</body>

2.8 生命周期鉤子函數(shù)

每個(gè)Vue實(shí)例在被創(chuàng)建的時(shí)候都要經(jīng)過(guò)一系列的初始化過(guò)程:創(chuàng)建實(shí)例、裝載模板、渲染模板等。Vue為生命周期中的每個(gè)狀態(tài)都設(shè)置了鉤子函數(shù)(即一個(gè)監(jiān)聽(tīng)函數(shù))。每當(dāng)Vue實(shí)例處于不同的生命周期時(shí),對(duì)應(yīng)的鉤子函數(shù)就會(huì)被觸發(fā)自動(dòng)調(diào)用。

生命周期:Vue對(duì)象的創(chuàng)建到銷(xiāo)毀

<body>
    <div id="div1">
        <span id="numSpan">{{num}}</span>
        <button @click="num++">點(diǎn)贊!</button>
        <h2>{{name}}, 有{{num}}個(gè)人點(diǎn)贊</h2>
    </div>
    
    <script src="./node_modules/vue/dist/vue.js"></script>  
        
    <script>
        let vm = new Vue({
            el: "#div1",
            data: {
                name: "zhangsan",
                num: 100
            },
            methods: {
                show() {
                    return this.name;
                },
                add() {
                    this.num++;
                }
            },
            // beforeCreate: 注入之前
            beforeCreate() {
                console.log("========= beforeCreate ========");
                console.log("數(shù)據(jù)模型未加載: " + this.name , this.num);
                console.log("方法未加載: " + this.show());
                console.log("html模板未加載: " + document.getElementById("numSpan"));
            },
            // beforeCreate: 注入之后
            created() {
                console.log("========= created ========");
                console.log("數(shù)據(jù)模型已加載: " + this.name , this.num);
                console.log("方法已加載: " + this.show());
                console.log("html模板已加載: " + document.getElementById("numSpan"));
                // 未渲染是指此時(shí)看到的還是{{num}}這樣一個(gè)插值表達(dá)式
                console.log("html模板未渲染: " + document.getElementById("numSpan").innerText);
            },
            // beforeMount: 掛載之前, 掛載即Vue實(shí)例與html元素綁定
            beforeMount() {
                console.log("========= beforeMount ========");
                console.log("html模板未渲染: " + document.getElementById("numSpan").innerText);
            },
            // mounted: 掛載之后
            mounted() {
                console.log("========= mounted ========");
                console.log("html模板已渲染: " + document.getElementById("numSpan").innerText);
            },
            // beforeUpdate: 數(shù)據(jù)更新之前
            beforeUpdate() {
                console.log("========= beforeUpdate ========");
                console.log("數(shù)據(jù)模型已更新: " + this.num);
                console.log("html模板未更新: " + document.getElementById("numSpan").innerText);
            },
            // updated: 數(shù)據(jù)更新之后
            updated() {
                console.log("========= updated ========");
                console.log("數(shù)據(jù)模型已更新: " + this.num);
                console.log("html模板已更新: " + document.getElementById("numSpan").innerText);
            }
        })
    </script>
</body>

2.9 Vue的模塊化開(kāi)發(fā)

2.9.1 Vue項(xiàng)目的安裝與啟動(dòng)

Step1:全局安裝webpack(直接在cmd命令行安裝即可)

npm install webpack -g

Step2:全局安裝Vue腳手架

npm install -g @vue/cli-init

Step3:初始化Vue項(xiàng)目

cmd進(jìn)入想要?jiǎng)?chuàng)建Vue項(xiàng)目的文件夾,輸入以下命令:

即以webpack為模板創(chuàng)建一個(gè)Vue項(xiàng)目

vue init webpack vuename(項(xiàng)目的名字)

Step4:?jiǎn)?dòng)Vue項(xiàng)目

cmd進(jìn)入Step3創(chuàng)建的項(xiàng)目名字的文件夾,輸入以下命令:

npm run dev

然后默認(rèn)訪(fǎng)問(wèn)本的8080端口可以查看到該項(xiàng)目

2.9.2 模塊化開(kāi)發(fā)

項(xiàng)目的文件夾結(jié)構(gòu):

  • build:和項(xiàng)目打包相關(guān)
  • config:配置信息,包含了項(xiàng)目的ip、端口號(hào)等配置
  • node_modules:項(xiàng)目中用到的所有依賴(lài)
  • src:我們編寫(xiě)代碼的文件夾
  • static:靜態(tài)資源文件,比如存放圖片文件
  • index.html:主入口頁(yè)面
  • package.json:依賴(lài)包信息(類(lèi)似Maven中的pom.xml)

index.html:只有一個(gè)id為app的div

<body>
    <div id="app"></div>
</body>

main.js:主程序

import Vue from 'vue'
import App from './App'
// . 表示當(dāng)前目錄
import router from './router'

Vue.config.productionTip = false

new Vue({
    el: "#app",
    // 我們自定義的路由名: 路由對(duì)象(定義了頁(yè)面跳轉(zhuǎn)的規(guī)則)
    router: router,
    // 我們自定義的組件名: 組件對(duì)象
    components{ App: App},
    // 指定模板為App組件
    template: '<App/>'
})

App.vue:?jiǎn)挝募M件,分為三部分template、script、style

<!-- 組件的模板 -->
<template>
    <div id="app">
        <img src="./assets/logo.png">
        <!-- 路由視圖
             即這里顯示的內(nèi)容要根據(jù)訪(fǎng)問(wèn)路徑來(lái)動(dòng)態(tài)決定 -->
        <router-view/>
    </div>
</template>

<!-- 以前Vue實(shí)例里寫(xiě)的代碼 -->
<script>
    export default {
        name: "App"
        data(): {
            return {
                ...
            }
        },
        methods: {...}
    }
</script>

<!-- 組件的樣式 -->
<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
</style>

router/index.js:定義路由規(guī)則

import Vue from 'vue'
import Router from 'vue-router'
// @ 表示src目錄
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

// 導(dǎo)出一個(gè)未命名的Router對(duì)象, 用于給main.js中導(dǎo)入
export default new Router({
    routes: [
        {
            path: '/',  // 訪(fǎng)問(wèn)的路徑
            name: 'HelloWorld',  // 給這個(gè)路由起的名
            component: HelloWorld  // 使用HelloWorld組件(HelloWorld是項(xiàng)目自帶的一個(gè)實(shí)例組件)
        }
    ]
})

2.9.3 自定義組件

創(chuàng)建一個(gè)自定義的組件Hellow.vue

<!-- 組件的模板 -->
<template>
    <div id="app">
        <h1>你好!Hellow!{{name}}</h1>
    </div>
</template>

<!-- 以前Vue實(shí)例里寫(xiě)的代碼 -->
<script>
    export default {
        name: "App"
        data(): {
            return {
                name: "Zhangsan"
            }
        }
    }
</script>

<!-- 組件的樣式 -->
<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
</style>>

然后在router/index.js中添加一條路由規(guī)則:

import Vue from 'vue'
import Router from 'vue-router'
// @ 表示src目錄
import HelloWorld from '@/components/HelloWorld'
import Hellow from '@/components/HelloWorld'

Vue.use(Router)

// 導(dǎo)出一個(gè)未命名的Router對(duì)象, 用于給main.js中導(dǎo)入
export default new Router({
    routes: [
        {
            path: '/',  // 訪(fǎng)問(wèn)的路徑
            name: 'HelloWorld',  // 給這個(gè)路由起的名
            component: HelloWorld  // 使用HelloWorld組件(HelloWorld是項(xiàng)目自帶的一個(gè)實(shí)例組件)
        },
        {
            path: '/hellow',  // 訪(fǎng)問(wèn)的路徑
            name: 'Hello',  // 給這個(gè)路由起的名
            component: Hello  // 使用我們自定義的Hellow組件
        }
    ]
})

如果想使用超鏈接來(lái)與路由視圖綁定,可以在App.vue中使用<router-link>標(biāo)簽來(lái)做超鏈接

<!-- 組件的模板 -->
<template>
    <div id="app">
        <img src="./assets/logo.png">
        <!-- 路由視圖
             即這里顯示的內(nèi)容要根據(jù)訪(fǎng)問(wèn)路徑來(lái)動(dòng)態(tài)決定 -->
        <router-link to="/hellow">去Hellow</router-link>
        <router-link to="/">去首頁(yè)</router-link>
        <router-view/>
    </div>
</template>

<!-- 以前Vue實(shí)例里寫(xiě)的代碼 -->
<script>
    export default {
        name: "App"
        data(): {
            return {
                ...
            }
        },
        methods: {...}
    }
</script>

<!-- 組件的樣式 -->
<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
</style>

2.10 Vue整合ElementUI快速開(kāi)發(fā)

ElementUI官方網(wǎng)站:http://element.eleme.cn

是一個(gè)幫我們整合了許多Vue常用組件的組件庫(kù),比如單選框、多選框、按鈕等。

Step1:用npm安裝ElementUI

npm i element-ui

Step2:在main.js中導(dǎo)入ElementUI組件庫(kù)

import Vue from 'vue'
import App from './App'
import router from './router'
// 導(dǎo)入ElementUI
import ElementUI from 'element-ui'
// 導(dǎo)入ElementUI的css樣式
import 'element-ui/lib/theme-chalk/index.css'

// 讓導(dǎo)入的組件庫(kù)生效(這樣在項(xiàng)目的任何組件中都可以使用了)
Vue.use(ElementUI);

Vue.config.productionTip = false

new Vue({
    el: "#app",
    router: router,
    components{ App: App},
    template: '<App/>'
})

Step3:測(cè)試:在Hellow.vue組件中使用ElementUI

<!-- 組件的模板 -->
<template>
    <div id="app">
        <h1>你好!Hellow!{{name}}</h1>
        
        <!-- 是ElementUI中的單選框組件 -->
        <el-radio v-model="radio" label="1">備選項(xiàng)1</el-radio>
        <el-radio v-model="radio" label="2">備選項(xiàng)2</el-radio>
    </div>
</template>

<!-- 以前Vue實(shí)例里寫(xiě)的代碼 -->
<script>
    export default {
        name: "App"
        data(): {
            return {
                name: "Zhangsan",
                // 這里給1, 單選框中默認(rèn)選中的就是備選項(xiàng)1, 官方文檔有說(shuō)明
                radio: "1"  
            }
        }
    }
</script>

<!-- 組件的樣式 -->
<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
</style>>

更多ElementUI組件模板代碼在ElementUI官方網(wǎng)站都有

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

相關(guān)閱讀更多精彩內(nèi)容

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