本節目錄
之前我們獲取dom是通過原生js或者jQuery的選擇器來獲取的,那么vue也給我們提供了一些獲取dom的方法。
方式:給標簽或者組件添加ref屬性,將來我們通過this.$refs屬性可以獲取到這個標簽或者組件。
<div ref="chao"></div> <p ref="a"></p> <Home ref="b"></Home> 子組件 <script> this.$refs.chao 獲取原始的DOM對象 this.$refs.b 父組件中獲取的是子組件實例化對象 </script>
簡單使用一下:讓某個input標簽在瀏覽器加載完之后,自動獲取到光標(焦點)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> </body> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> Vue.component('Test',{ data(){ return{ } }, //如果這個全局組件中沒有template或者render方法,會報個錯誤給你,這個補充和咱們今天的內容沒有關系昂 template: ` <div>這是全局Test組件</div> `, }) ; let App = { data(){ return{ } }, template:` <div> <input ref="username" type="text"> <Test ref="abc"></Test> </div> `, //讓上面的input默認是獲取焦點的狀態,剛開始打開頁面的時候,input是沒有獲取到焦點(光標)的狀態 mounted(){ //操作dom的方式 console.log(this.$refs.username); this.$refs.username.focus();//focus()方法是原生js方法,讓前面這個input標簽自動獲取焦點用的。 //操作組件的方式 console.log(this.$refs);//所有含有ref屬性的標簽或者組件:{username: input, abc: VueComponent},發現就是個自定義屬性(字典) console.log(this.$refs.abc);//VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} console.log(this.$refs['abc']); //VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} console.log(this.$refs.abc.$parent);//就是this,abc的父級組件App組件 console.log(this.$children);//就是this當前的組件的所有子組件,順序是按照template中使用組件的時候的順序,還有好多其他的方法等等,以后再說 //循環所有含有ref屬性的標簽或者組件 for (let key in this.$refs){ console.log(this.$refs[key]); } }, }; let vm = new Vue({ el:'#app', data(){ return{ } }, template:` <div> <App></App> </div> `, components:{ App, } }) </script> </html>
首先我們先到YEOMAN中看一個圖,這個YEOMAN是集合了前端所有的腳手架,包括服務器的、js的、jQuery的、react、angular、vue等等的腳手架都在這里面有,網址:https://yeoman.io/,我們不用去研究里面的內容,我只想讓大家看一個圖:

這個圖的意思就是每個工程師都拿着自己現成的工具來做火箭這個項目,將來我們要通過vue-cli來做組件化開發,在組件化開發中就使用到了我們的webpack工具(這是前端的工具),前端最開始用的grunt后來是gulp,再后來就是我們現在主流的webpack工具了,那到底這個工具做了什么,我們往后看。
webpack介紹
webpack是一個現代JavaScript應用程序的靜態模塊打包器(比如python中一個.py文件就是一個模塊,那么前端中一個js、html、css等文件就可以稱為一個模塊)。當 webpack 處理應用程序時,它會遞歸地構建一個*依賴關系圖(dependency graph)*,其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 *bundle*。這個webpack已經內置在了vue-cli中,所以我們下載安裝了vue-cli之后就能直接使用webpack工具了。接下來我們看一下下面這張圖,圖片來自於webpack官網(中文文檔)https://www.webpackjs.com/

歷史介紹(了解)
- 2009年初,commonjs異步模塊規范還未出來,此時前端開發人員編寫的代碼都是非模塊化的,使用閉包來完成類似模塊化的開發。commonjs是在nodejs中產生的,是服務器語言,也就是有他之后,前端語言就可以做服務端的開發了。 - 那個時候開發人員經常需要十分留意文件加載順序所帶來的依賴問題,比如你使用很多的script標簽,然后每個標簽的src屬性指向一個js文件,那么要注意這些標簽的引入順序,並且有一個js文件出問題了,后面的js文件都加載不上,這就是一個同步加載的問題。 - 與此同時 nodejs開啟了js全棧大門,而requirejs在國外也帶動着前端逐步實現模塊化 - 同時國內seajs也進行了大力推廣,中國人淘寶玉伯寫的,他的規范叫做AMD - AMD 規范 ,具體實現是requirejs define('模塊id',[模塊依賴1,模塊依賴2],function(){ return ;}) , ajax請求文件並加載,例如:你有三個js文件,a.js、b.js、c.js,使用c.js的時候需要依賴a和b,那么寫法 define('c.js',(a.js,b.js),function(){}) - Commonjs || CMD規范 - commonjs和cmd非常相似的 - cmd require(類似於import,引入的意思)/module.exports(輸出的意思)
- commonjs是js在后端語言的規范: 模塊、文件操作、操作系統底層 - CMD 僅僅是模塊定義 - UMD 通用模塊定義,一種既能兼容amd也能兼容commonjs 也能兼容瀏覽器環境運行的萬能代碼 - npm/bower集中包管理的方式備受青睞,12年browserify/webpack誕生 - npm 是可以下載前后端的js代碼475000個包 - bower 只能下載前端的js代碼,bower 在下載bootstrap的時候會自動的下載jquery - browserify 解決讓require可以運行在瀏覽器,分析require的關系,組裝代碼 - webpack 打包工具,占市場主流
后面的內容需要我們學習~~~
1. require和module.exports
require和module.exports的使用舉例,注意這兩個方法必須在nodejs的環境下才能使用,不然瀏覽器是識別不了這兩個方法,就會報錯,並且我們平時創建的.js文件在nodejs環境中會識別成nodejs類型的文件,不過沒關系,基本都是支持的,創建文件的時候還是xx.js文件。
假如我們現在有三個文件,index.html和time.js和index.js文件
index.html最開始引入js文件的方法是這樣的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 同步引入的方式 --> <script src="index.js"></script> <script src="time.js"></script> </body> </html>
然后我們通過CMD規范來寫一下通過require和module.exports兩個方法實現模塊化開發
index.js內容如下:
// var person1 = { // name:'張三', // // }; // //可以拋出,自定義對象,變量,函數等等內容,但是這些js文件的這樣的用法必須在nodejs環境下執行,而nodejs是從原生js加工出來的,支持我們寫js語言,但是有些功能不支持,比如說alert,window彈框等操作 // var person2 = { // name:'李四', // }; //將我們在index.js中的person拋出,然后其他js文件就可以通過require方法來引用了 // module.exports = person1; function add(){ console.log('add函數'); } module.exports = add; //將add函數拋出,其他js文件才能通過require引入使用,將來我們就將我們的組件寫到一個js文件中,通過這個拋出組件,然后其他文件中require來使用這個組件
time.js內容如下,time.js文件的運行需要index.js中的內容
var p = require('./index.js'); // console.log(p.name); p();//執行一下index.js文件中引入的add函數
說了,我們需要在nodejs的環境下使用這兩個方法,那么我們在pycharm這個IDE的終端來調用一下node來執行一下time.js文件。
先看我的文件目錄結構:

然后終端切換到這個nodejs模塊的文件夾下,執行一下下面的指令:就得到了index.js函數中的執行結果。

上面就是CMD的規范,其實vue更加希望我們使用es6的module(模塊)規范,我們來看看module規范的語法。
一個export拋出,一個import引入,阮一峰的博客上介紹的比較詳細,就在他es6的那個博客里面有個module語法的那個章節里面有。
好,那么我們先來學一下export和import的應用:
2.export和import
簡單鋪墊:歷史上,JavaScript 一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來。其他語言都有這項功能,比如 Ruby 的require、Python 的import,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支持都沒有,這對開發大型的、復雜的項目形成了巨大障礙。
在 ES6 之前,社區制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種。前者用於服務器,后者用於瀏覽器。ES6 在語言標准的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規范,成為瀏覽器和服務器通用的模塊解決方案。
ES6 模塊的設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時確定這些東西。比如,CommonJS 模塊就是對象,輸入時必須查找對象屬性。
// CommonJS模塊 let { stat, exists, readFile } = require('fs'); // 等同於 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
上面代碼的實質是整體加載fs模塊(即加載fs的所有方法),生成一個對象(_fs),然后再從這個對象上面讀取 3 個方法。這種加載稱為“運行時加載”,因為只有運行時才能得到這個對象,導致完全沒辦法在編譯時做“靜態優化”。
ES6 模塊不是對象,而是通過export命令顯式指定輸出的代碼,再通過import命令輸入。
// ES6模塊 import { stat, exists, readFile } from 'fs';
上面代碼的實質是從fs模塊加載 3 個方法,其他方法不加載。這種加載稱為“編譯時加載”或者靜態加載,即 ES6 可以在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。當然,這也導致了沒法引用 ES6 模塊本身,因為它不是對象。
由於 ES6 模塊是編譯時加載,使得靜態分析成為可能。有了它,就能進一步拓寬 JavaScript 的語法,比如引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。
export命令
模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。
一個模塊就是一個獨立的文件。該文件內部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。下面是一個 JS 文件,里面使用export命令輸出變量。
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958;
上面代碼是profile.js文件,保存了用戶信息。ES6 將其視為一個模塊,里面用export命令對外部輸出了三個變量。
export的寫法,除了像上面這樣,還有另外一種。
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
上面代碼在export命令后面,使用大括號指定所要輸出的一組變量。它與前一種寫法(直接放置在var語句前)是等價的,但是應該優先考慮使用這種寫法。因為這樣就可以在腳本尾部,一眼看清楚輸出了哪些變量。
export命令除了輸出變量,還可以輸出函數或類(class)。
export function multiply(x, y) { return x * y; };
上面代碼對外輸出一個函數multiply。
通常情況下,export輸出的變量就是本來的名字,但是可以使用as關鍵字重命名。
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
上面代碼使用as關鍵字,重命名了函數v1和v2的對外接口。重命名后,v2可以用不同的名字輸出兩次。
需要特別注意的是,export命令規定的是對外的接口,必須與模塊內部的變量建立一一對應關系。
// 報錯 export 1; // 報錯 var m = 1; export m;
上面兩種寫法都會報錯,因為沒有提供對外的接口。第一種寫法直接輸出 1,第二種寫法通過變量m,還是直接輸出 1。1只是一個值,不是接口。正確的寫法是下面這樣。
// 寫法一 export var m = 1; // 寫法二 var m = 1; export {m}; // 寫法三 var n = 1; export {n as m};
上面三種寫法都是正確的,規定了對外的接口m。其他腳本可以通過這個接口,取到值1。它們的實質是,在接口名與模塊內部變量之間,建立了一一對應的關系。
同樣的,function和class的輸出,也必須遵守這樣的寫法。
// 報錯 function f() {} export f; // 正確 export function f() {}; // 正確 function f() {} export {f};
另外,export語句輸出的接口,與其對應的值是動態綁定關系,即通過該接口,可以取到模塊內部實時的值。
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
上面代碼輸出變量foo,值為bar,500 毫秒之后變成baz。
這一點與 CommonJS 規范完全不同。CommonJS 模塊輸出的是值的緩存,不存在動態更新,詳見下文《Module 的加載實現》一節。
最后,export命令可以出現在模塊的任何位置,只要處於模塊頂層就可以。如果處於塊級作用域內,就會報錯,下一節的import命令也是如此。這是因為處於條件代碼塊之中,就沒法做靜態優化了,違背了 ES6 模塊的設計初衷。
function foo() { export default 'bar' // SyntaxError } foo()
上面代碼中,export語句放在函數之中,結果報錯。
import命令
使用export命令定義了模塊的對外接口以后,其他 JS 文件就可以通過import命令加載這個模塊。
// main.js import {firstName, lastName, year} from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
上面代碼的import命令,用於加載profile.js文件,並從中輸入變量。import命令接受一對大括號,里面指定要從其他模塊導入的變量名。大括號里面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。
如果想為輸入的變量重新取一個名字,import命令要使用as關鍵字,將輸入的變量重命名。
import { lastName as surname } from './profile.js';
import命令輸入的變量都是只讀的,因為它的本質是輸入接口。也就是說,不允許在加載模塊的腳本里面,改寫接口。
import {a} from './xxx.js' a = {}; // Syntax Error : 'a' is read-only;
上面代碼中,腳本加載了變量a,對其重新賦值就會報錯,因為a是一個只讀的接口。但是,如果a是一個對象,改寫a的屬性是允許的。
import {a} from './xxx.js' a.foo = 'hello'; // 合法操作
上面代碼中,a的屬性可以成功改寫,並且其他模塊也可以讀到改寫后的值。不過,這種寫法很難查錯,建議凡是輸入的變量,都當作完全只讀,輕易不要改變它的屬性。
import后面的from指定模塊文件的位置,可以是相對路徑,也可以是絕對路徑,.js后綴可以省略。如果只是模塊名,不帶有路徑,那么必須有配置文件,告訴 JavaScript 引擎該模塊的位置。
import {myMethod} from 'util';
上面代碼中,util是模塊文件名,由於不帶有路徑,必須通過配置,告訴引擎怎么取到這個模塊。
注意,import命令具有提升效果,會提升到整個模塊的頭部,首先執行。
foo(); import { foo } from 'my_module';
上面的代碼不會報錯,因為import的執行早於foo的調用。這種行為的本質是,import命令是編譯階段執行的,在代碼運行之前。
由於import是靜態執行,所以不能使用表達式和變量,這些只有在運行時才能得到結果的語法結構。
// 報錯 import { 'f' + 'oo' } from 'my_module'; // 報錯 let module = 'my_module'; import { foo } from module; // 報錯 if (x === 1) { import { foo } from 'module1'; } else { import { foo } from 'module2'; }
上面三種寫法都會報錯,因為它們用到了表達式、變量和if結構。在靜態分析階段,這些語法都是沒法得到值的。
最后,import語句會執行所加載的模塊,因此可以有下面的寫法。
import 'lodash';
上面代碼僅僅執行lodash模塊,但是不輸入任何值。
如果多次重復執行同一句import語句,那么只會執行一次,而不會執行多次。
import 'lodash'; import 'lodash';
上面代碼加載了兩次lodash,但是只會執行一次。
import { foo } from 'my_module'; import { bar } from 'my_module'; // 等同於 import { foo, bar } from 'my_module';
上面代碼中,雖然foo和bar在兩個語句中加載,但是它們對應的是同一個my_module實例。也就是說,import語句是 Singleton 模式。
目前階段,通過 Babel 轉碼,CommonJS 模塊的require命令和 ES6 模塊的import命令,可以寫在同一個模塊里面,但是最好不要這樣做。因為import在靜態解析階段執行,所以它是一個模塊之中最早執行的。下面的代碼可能不會得到預期結果。
require('core-js/modules/es6.symbol'); require('core-js/modules/es6.promise'); import React from 'React';
模塊整體加載
除了指定加載某個輸出值,還可以使用整體加載,即用星號(*)指定一個對象,所有輸出值都加載在這個對象上面。
下面是一個circle.js文件,它輸出兩個方法area和circumference。
// circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; }
現在,加載這個模塊。
// main.js import { area, circumference } from './circle'; console.log('圓面積:' + area(4)); console.log('圓周長:' + circumference(14));
上面寫法是逐一指定要加載的方法,整體加載的寫法如下。
import * as circle from './circle'; console.log('圓面積:' + circle.area(4)); console.log('圓周長:' + circle.circumference(14));
注意,模塊整體加載所在的那個對象(上例是circle),應該是可以靜態分析的,所以不允許運行時改變。下面的寫法都是不允許的。
import * as circle from './circle'; // 下面兩行都是不允許的 circle.foo = 'hello'; circle.area = function () {};
export default命令
從前面的例子可以看出,使用import命令的時候,用戶需要知道所要加載的變量名或函數名,否則無法加載。但是,用戶肯定希望快速上手,未必願意閱讀文檔,去了解模塊有哪些屬性和方法。
為了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,為模塊指定默認輸出。
// export-default.js export default function () { console.log('foo'); }
上面代碼是一個模塊文件export-default.js,它的默認輸出是一個函數。
其他模塊加載該模塊時,import命令可以為該匿名函數指定任意名字。
// import-default.js import customName from './export-default'; customName(); // 'foo'
上面代碼的import命令,可以用任意名稱指向export-default.js輸出的方法,這時就不需要知道原模塊輸出的函數名。需要注意的是,這時import命令后面,不使用大括號。
export default命令用在非匿名函數前,也是可以的。
// export-default.js export default function foo() { console.log('foo'); } // 或者寫成 function foo() { console.log('foo'); } export default foo;
上面代碼中,foo函數的函數名foo,在模塊外部是無效的。加載的時候,視同匿名函數加載。
下面比較一下默認輸出和正常輸出。
// 第一組 export default function crc32() { // 輸出 // ... } import crc32 from 'crc32'; // 輸入 // 第二組 export function crc32() { // 輸出 // ... }; import {crc32} from 'crc32'; // 輸入
上面代碼的兩組寫法,第一組是使用export default時,對應的import語句不需要使用大括號;第二組是不使用export default時,對應的import語句需要使用大括號。
export default命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,因此export default命令只能使用一次。所以,import命令后面才不用加大括號,因為只可能唯一對應export default命令。
本質上,export default就是輸出一個叫做default的變量或方法,然后系統允許你為它取任意名字。所以,下面的寫法是有效的。
// modules.js function add(x, y) { return x * y; } export {add as default}; // 等同於 // export default add; // app.js import { default as foo } from 'modules'; // 等同於 // import foo from 'modules';
正是因為export default命令其實只是輸出一個叫做default的變量,所以它后面不能跟變量聲明語句。
// 正確 export var a = 1; // 正確 var a = 1; export default a; // 錯誤 export default var a = 1;
上面代碼中,export default a的含義是將變量a的值賦給變量default。所以,最后一種寫法會報錯。
同樣地,因為export default命令的本質是將后面的值,賦給default變量,所以可以直接將一個值寫在export default之后。
// 正確 export default 42; // 報錯 export 42;
上面代碼中,后一句報錯是因為沒有指定對外的接口,而前一句指定對外接口為default。
有了export default命令,輸入模塊時就非常直觀了,以輸入 lodash 模塊為例。
import _ from 'lodash';
如果想在一條import語句中,同時輸入默認方法和其他接口,可以寫成下面這樣。
import _, { each, forEach } from 'lodash';
對應上面代碼的export語句如下。
export default function (obj) { // ··· } export function each(obj, iterator, context) { // ··· } export { each as forEach };
上面代碼的最后一行的意思是,暴露出forEach接口,默認指向each接口,即forEach和each指向同一個方法。
export default也可以用來輸出類。
// MyClass.js export default class { ... } // main.js import MyClass from 'MyClass'; let o = new MyClass();
export 和 import的復合用法
如果在一個模塊之中,先輸入后輸出同一個模塊,import語句可以與export語句寫在一起。
export { foo, bar } from 'my_module';
// 可以簡單理解為
import { foo, bar } from 'my_module';
export { foo, bar };
上面代碼中,export和import語句可以結合在一起,寫成一行。但需要注意的是,寫成一行以后,foo和bar實際上並沒有被導入當前模塊,只是相當於對外轉發了這兩個接口,導致當前模塊不能直接使用foo和bar。
模塊的接口改名和整體輸出,也可以采用這種寫法。
// 接口改名 export { foo as myFoo } from 'my_module'; // 整體輸出 export * from 'my_module';
默認接口的寫法如下。
export { default } from 'foo';
具名接口改為默認接口的寫法如下。
export { es6 as default } from './someModule';
// 等同於
import { es6 } from './someModule';
export default es6;
同樣地,默認接口也可以改名為具名接口。
export { default as es6 } from './someModule';
下面三種import語句,沒有對應的復合寫法。
import * as someIdentifier from "someModule"; import someIdentifier from "someModule"; import someIdentifier, { namedIdentifier } from "someModule";
為了做到形式的對稱,現在有提案,提出補上這三種復合寫法。
export * as someIdentifier from "someModule"; export someIdentifier from "someModule"; export someIdentifier, { namedIdentifier } from "someModule";
好,總結一下,export和export default都是拋出接口的,而import是獲取接口的。
下面我們就通過export default先來個例子玩玩:
我們先創建兩個文件,main.js文件和index.html文件和module.js文件
index.html內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 只引入main.js,因為main.js用來作為所有js文件的入口文件 --> <script src="main.js"></script> </body> </html>
main.js內容如下:
//這個文件里面我們可能會寫很多的js代碼,並且現在我們的場景是這個文件里面的代碼依賴一些module.js文件中的內容 import person from './module.js' //person這個名字隨便起,因為export default把他們都放到了一個default變量上,任何變量名都可以接收到 console.log(person.name); console.log(person.f1());
module.js內容如下:
var person = { name:'張三', f1:function(){ alert(1); //彈出一個1對話框 } }; export default person;
但是我們通過瀏覽器打開這個index.html文件,你會發現報錯了,瀏覽器不支持import等的寫法,既然瀏覽器不支持這樣的模塊化,那怎么辦呢,這就要借助我們的webpack工具了。
我說過webpack需要找到我們所有js文件(或者其他文件比如css文件)的一個入口文件,然后將所有的文件(js\css等)打包輸出成為一個js文件,下面我們就來看看,如何通過webpack進行打包並實現模塊化開發,記住一點,webpack工具必須在nodejs的環境下才能使用,這也是為什么我們要先下載nodejs。
看webpack的用法,我們先在pycharm的終端指令中執行一個webpack指令:

那么main.js和module.js文件內容不變,index.html文件中的引入改一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 只引入main.js,因為main.js用來作為所有js文件的入口文件 --> <!--<script src="main.js"></script>--> <!-- 直接引用我們通過webpack打包好的js文件 --> <script src="bundle.js"></script> </body> </html>
然后通過瀏覽器打開我們的index.html文件,效果就出來了,

具體的這個打包出來的文件內容,你暫時不需要理解,會用就可以了。
接下來我們使用一下export來拋出多個內容:
main.js的寫法:
// export拋出后我們import來接收的方式:console.log(person,num); import {person,num} from './module.js' console.log(person,num) //但是這里要注意的就是,person和num要和別的文件拋出的變量名字相同 //一下接收所有的變量,*,但是*符號和我們的css有沖突,所以搞個別名,我這里起了個別名叫a import * as a from './module.js' console.log(a);
module.js的寫法
var person = { name:'張三', f1:function(){ alert(1); } }; var num = 32; // export default person; export {person,num}; //通過export拋出多個變量,還能拋出函數,類等內容,我這里就沒有寫啦
index.html文件不用改:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 直接引用我們通過webpack打包好的js文件 --> <script src="bundle.js"></script> </body> </html>
別忘了,我們將js文件修改了,那么我們需要重新執行一下打包過程,webpack ./main.js ./bundle.js,中間的空格別忘了寫,有朋友可能要說了,每次都手動打包一下嗎?后面咱們就不用手動打包了,會有自動打包的方法,往后面學。
然后瀏覽器打開index.html文件看效果:在瀏覽器調試台的console的地方看

簡單總結一下:
CMD:
拋出:module.exports = xxx;
引用:require()
ES6:
拋出:export export var num=10; export function add(){} var n = 100; var s = 'chao'; export {n,s} 引用: import * as a from './module.js' //as起別名 拋出:export default var person = { name:'張三', f1:function(){ alert(1); } }; export default person; 引用: import xxx from './module.js' xxx.name; xxx.f1();
大家現在為止是不是覺得很麻煩啊,每次都需要自行webpack打包一下,每次改動都要打包,那么腳手架提供給我們方便我們使用的框架,像這種 打包的事情就不需要自己做了,上面我們學習的export和import就是我們進行模塊化開發的基礎。
還記得我們上面自己下載的vue.js嗎,這個文件是默認支持模塊化開發的,下面我們看一個例子:
看我文件的目錄結構:

其中main.js是我們要寫的js代碼,內容如下:
import Vue from './vue.js' //我們想要進行模塊化開發,那么要做的就是將每個組件單獨放到一個js文件中,而不是在這里在let一個組件了,當然我們可以寫在這個js文件中,但是每個組件里面可能還嵌套着其他的組件並且有很多其他的結構和代碼,如果都寫在這一個js文件中,會很亂,很難維護,所以我們爭取將這些組件進行解耦,所以你看我們又創建了一個App.js文件,文件名稱我用首字母大寫來寫的,目的是讓別人知道這是個組件 // let App = {}; // 引入我們寫的App組件 import App from './App.js' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue實例對象</div> <App></App> </div> `, components:{ App, } });
App.js內容如下:
//定義一個組件 let App = { template: ` <div> 我是App組件 </div> ` }; //將組件拋出 export default App;
index.html文件內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <script src="bundle.js"></script> </body> </html>
然后我們執行webpack指令打包一下js文件,js入口文件就是我們的main.js,因為都是通過main.js引入的其他的js文件中的內容,首先在終端cd切換到我們的存放這些文件的文件夾下,然后執行指令:webpack ./main.js ./bundle.js,然后在index.html文件中引入一下我們打包出來的bundle.js文件。
然后通過瀏覽器打開我們的index.html文件,就看到了效果:

此時我們發現,我們每次改動js文件都要在終端輸入webpack指令來打包一下,比較麻煩:

所以我們需要用一個簡單的方式來執行webpack指令,打包我們的文件,那么我們就來學一下webpack的使用:
1.需要安裝nodejs,這個我們已經安裝好了
2.執行npm init --yes 指令(不加--yes,你看看是什么效果),默認會生成一個package.json文件(管理整個項目中的包資源,類似於我們python的django框架中的settings.py配置文件,配置某些東西用的)
首先,修改一下我們的目錄結構,把項目文件夾名稱改為英文的(不然會有一些編碼錯誤問題):

然后我們在我們的項目目錄下執行一下這個指令:其實這個指令也不要記了,后面我們會有一鍵生成這個package.json文件的操作。

package.json文件創建好了之后,我們就需要用它來管理我們的包了,首先我們要在我們的項目里面下載webpack包(之前webpack我們是安裝在了全局,現在是搞到我們的項目里面,因為以后你的項目要打包上線的,現上不一定有你webpack的工具),執行指令npm i webpack@3.12.0 -D (-D,開發環境依賴,寫上就行了),執行完這個指令之后,我們的項目目錄下就多了這個node_modules文件夾,將來你這個項目打包上線,我們這個腳手架以及webpack工具需要的依賴就都在這個文件夾里面了,直接把它也打包給我們的線上環境。

然后看一下我們的package.json文件:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { //現在主要看這里,將來我們執行num run test,就會執行這個scripts屬性里面的test屬性對應的后面的腳本指令 "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
好,既然如此,我們想通過npm run dev,來執行webpack ./main.js ./bundle.js這個命令,寫起來就簡單多了,所以我們需要配置一下上面這個文件中的scripts里面的屬性,看配置:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { // "test": "echo \"Error: no test specified\" && exit 1", 注意,你使用我這個配置的時候,把我寫的這些注釋全部去掉,不然沒辦法打包編譯,會出錯 "dev": "webpack ./main.js ./bundle.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //這是我們當前項目里面有的開發工具或者說是開發依賴,此時我們的工具有一個,叫做webpack,將來如果需要其他的工具,我們安裝一下,這個地方就會顯示出來,此時我們只安裝了webpack,所以只顯示了這個webpack的信息。 "webpack": "^3.12.0" } }
然后我們把剛才生成的bundle.js文件刪掉,然后執行一下num run dev指令,那么這個指令就會執行我們在package.json中配置的scripts屬性中的dev屬性對應的值,也就是我們的那個webpack指令,那么同樣會生成我們的出口文件bundle.js ,看效果:


好,我們通過npm run dev,代替了我們我們之前寫的打包指令webpack ./main.js ./bundle.js,下面我們玩點webpack更高端的用法。
我們現在通過webpack想DIY一個腳手架,那么我們必須知道webpack的四個核心概念:
1.入口(entry):就是入口文件
2.出口(output):出口文件
3.loader :文件類型轉換器
4.插件(plugins)
看webpack中文文檔:

文檔里面有關於上面四個核心概念的解釋及應用。
入口和出口:
入口起點(entry point)指示 webpack 應該使用哪個模塊,來作為構建其內部依賴圖的開始。進入入口起點后,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。
每個依賴項隨即被處理,最后輸出到稱之為 bundles 的文件中,我們將在下一章節詳細討論這個過程。
可以通過在 webpack 配置中配置 entry 屬性,來指定一個入口起點(或多個入口起點)。默認值為 ./src。
出口(output) 屬性告訴 webpack 在哪里輸出它所創建的 bundles,以及如何命名這些文件,默認值為 ./dist。基本上,整個應用程序結構,都會被編譯到你指定的輸出路徑的文件夾中。你可以通過在配置中指定一個 output 字段,來配置這些處理過程。下面我們來搞一搞。
首先創建一個名為webpack.config.js的文件,必須是這個名字,這個文件就是用來配置我們上面說的webpack的四個核心概念(功能)的地方,那么先配置一下入口和出口,里面寫下面的內容:
module.exports = { //入口配置 entry: { 'main':'./main.js', //入口文件路徑 }, //出口配置 output:{ 'filename':'./bundle.js', //出口文件名稱和路徑 } };
寫完這些東西后,我們的package.json中的scripts的地方也需要修改一下:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { // "dev": "webpack ./main.js ./bundle.js" //配置完webpack.config.js文件之后,這里就不這樣寫了,使用我這個內容的時候,別忘了將注釋都去掉 "dev": "webpack" //執行npm run dev,那么就會找到webpack指令,並且這個webpack指令會找到webpack.config.js里面的配置,來執行打包指令 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
然后我們執行npm run dev指令,照樣生成我們的bundle.js文件,打開我們的index.html文件照樣能夠實現頁面效果。
但是又一想,每次修改了我們js文件里面的代碼,比如修改了App.js組件里面的代碼,我們都需要手動的執行一下這個npm run dev的打包指令,比較麻煩,我想讓它自動執行怎么辦,需要我們在webpack.config.js文件中配置一個watch屬性,看配置:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路徑 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名稱和路徑 }, //監聽,實時監聽代碼的改動,一旦改動了,自動進行打包,那么就不要我們手動執行npm run dev指令了 watch: true, };
那么我們只需要再執行一次npm run dev打包指令,以后我們修改什么App.js等文件的代碼之后,程序會自動會執行這個打包指令,來完成實時檢測代碼改動自動打包文件的效果,看操作:
首先執行一下npm run dev,你會發現終端的地方卡住了沒有結束(因為在實時監聽),然后通過瀏覽器打開index.html文件看效果:

然后,我們改一下App.js這個組件中的代碼:
//定義一個組件 let App = { template: ` <div> 我是App組件aaaaa //之前這里是沒有aaaaa的 </div> ` }; //將組件拋出 export default App;
之前我們改完代碼,都需要再執行npm run dev打包指令,現在我們就不需要了,直接到瀏覽器上刷新我們的index.html看效果:

那么我們以后在我們的開發環境中,只要配置好了webpack.config.js文件,那么就執行一次打包指令,以后其他js文件中有任何的改動,都會自動打包,省去了我們手動執行打包指令的操作,方便了很多。
然后你可能又想,我們開發環境中的關於webpack的配置有可能和我們生產環境中使用這個項目的時候的配置不太一樣,所以我們能不能單獨給開發環境和生產環境都做一個配置呢,也就是准備兩個webpack.config.js文件。
當然可以了啦,往下看,首先我們准備兩個配置文件,名為webpack.dev.config.js(給開發環境准備的)和webpack.pro.config.js(給生產環境准備的),文件名字可以隨便起昂,兩個文件我都用的相同的配置,這里只是給大家演示一下。
webpack.dev.config.js內容如下:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路徑 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名稱和路徑 }, //監聽,實時監聽代碼的改動,一旦改動了,自動進行打包,那么就不要我們手動執行npm run dev指令了 watch: true, };
webpack.pro.config.js內容如下:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路徑 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名稱和路徑 }, //監聽,實時監聽代碼的改動,一旦改動了,自動進行打包,那么就不要我們手動執行npm run dev指令了 watch: true, };
好,我們的文件配置完了,那么我們需要修改一下package.json文件中關於webpack指令的配置:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": {
//修改了這里:dev指令我們在開發的時候用,build我們在生產環境中一次性打包的時候使用, "dev": "webpack --config ./webpack.dev.config.js", //執行npm run dev,會找這個路徑的配置文件 "build": "webpack --config ./webpack.pro.config.js" //執行npm run build,會找這個路徑的配置文件 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
然后將我們的bundle.js文件和我們之前的webpack.config.js刪了,然后我們再執行npm run dev看看效果,然后刪除生成的bundle.js文件,然后再執行npm run build看看效果,就完成了我們給生產環境和開發環境的單獨配置,生產環境我們執行npm run build,開發環境我們執行npm run dev。
然后我們將我們的index.html和打包編譯好的bundle.js文件給服務器就可以拿去上線了。
再想,我們現在是不是只完成了js文件的打包啊,也就js文件的模塊化開發,那我們的項目還需要css、圖片等好多其他類型的文件呢,怎么搞,能不能通過webpack一起打包呢,可以的,往下看。
首先,我們創建一個index.css文件,內容如下;
body{ background-color: red; }
之前我們使用這個css文件是在我們的html文件的head標簽中通過link標簽的來引入,但是我們現在想,能不能將這個文件也通過打包,一起打包到我們的bundle.js文件中,然后讓html文件來使用呢?看騷操作:
首先我們說我們執行webpack打包指令的時候,打包的文件都是從一個入口文件開始打包的,因為入口文件里面是開始引入其他文件的最開始的地方,也就是我們前面創建的那個main.js文件,那么好,我們需要在這個入口也引入一下這個css文件,
看main.js文件的內容:
import Vue from './vue.js' import App from './App.js' import './index.css' //關於文件的引入,直接就這樣引入就可以了,上面兩個的引入是因為我們引入的是文件中的變量,而這里我們直接引入的是文件,所以寫法上略有不同 new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue實例對象</div> <App></App> </div> `, components:{ App, } });
那么此時我們還需要webpack的第三個核心概念的配置,loader轉換器,其實對於webpack來說css文件也是一個模塊。
loader介紹
loader 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉換為 webpack 能夠處理的有效模塊,然后你就可以利用 webpack 的打包能力,對它們進行處理。
本質上,webpack loader 將所有類型的文件,轉換為應用程序的依賴圖(和最終的 bundle)可以直接引用的模塊。
好,首先我們需要下載配置一些對應文件類型的loader轉換器,對於css文件的loader,我們需要下載這兩個loader:css-loader和style-loader,css-loader是解析css文件的,而style-loader是解析style標簽的,是將css文件中的css屬性內容放到style標簽中,並將這個style標簽放到html文件的head標簽的轉換器,執行下面的指令:
npm i css-loader style-loader -D
執行指令之后,你看一下package.json文件,你就會發現這個package.json文件里面多了一些內容,這下你就該明白這個文件是干什么的了吧,看文件內容
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //這里面多了這兩個loader,及對應的版本信息 "css-loader": "^2.1.1", "style-loader": "^0.23.1", "webpack": "^3.12.0" } }
然后我們還需要在我們的webpack.dev.config.js和webpack.pro.config.js文件中做一些loader的相關配置,我就把一個文件的內容給大家列舉出來吧,兩個文件現在是一樣的配置,看webpack.dev.config.js的內容:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路徑 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名稱和路徑 }, //監聽,實時監聽代碼的改動,一旦改動了,自動進行打包,那么就不要我們手動執行npm run dev指令了 watch: true,
//這是關於webpack的核心指令中的loader的配置 module: { loaders: [ { test: /\.css$/, //正則表達式 loader: 'style-loader!css-loader' //注意寫法,!的作用是先執行后面的loader再執行前面的loader // 遇到后綴為.css的文件,webpack先用css-loader加載器去解析這個文件 // 最后計算完的css,將會使用style-loader生成一個內容為最終解析完的css代碼的style標簽,放到head標簽里。 // webpack在打包過程中,遇到后綴為css的文件,就會使用style-loader和css-loader去加載這個文件。 } ] } };
修改了配置文件之后,我們還是需要執行npm run dev指令的,執行一下,然后瀏覽器打開index.html文件,你就看到css的效果了:

好,現在我們看到了body的背景顏色已經有了,那么如果是圖片呢,我們怎么玩,接着看,首先我們先到網上下載一個圖片放到本地,放到我們的項目目錄下,或者用圖片的網絡地址
首先,我們先看看在哪里顯示圖片呢,我們現在就App.js這個插件,那我們就在這個插件里面顯示圖片把,所以我們現在修改一下App.js這個文件內容,看代碼:
//引入本地圖片文件,將圖片內容作為一個變量引入,也就是給這個圖片對象起了個變量名,后面我們要通過這個變量名來使用這個圖片 import imgSrc from './meinv.jpg' //定義一個組件 let App = { data(){ return{ img:imgSrc,//使用數據屬性來接收一下圖片對象,然后在template中使用一下 } }, template: ` <div> 我是App組件aaaaa <div>
<!-- 執行了后面的npm run dev指令之后,默認會將我們的圖片的名稱用base64來編碼,生成一個新的base64編碼名字的圖片文件 --> <img :src="img" alt="">
<img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554823994028&di=db5686fc98db2a66a0b4738645922f09&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F170614%2F1311511X7-2.jpg" alt="" width="200" height="200"> </div> </div> `, }; //將組件拋出 export default App;
然后下載安裝一下對應的loader,執行下面的指令:
npm i url-loader file-loader -D
然后看一下package.json文件的內容,又自動增加了一下內容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", //多了它 "style-loader": "^0.23.1", "url-loader": "^1.1.2", //多了它 "webpack": "^3.12.0" } }
然后在我們的webpack.dev.config.js和webpack.pro.config.js中添加一下我們的圖片loader相關配置
module.exports = { entry: { 'main': './main.js', }, output: { 'filename': './bundle.js', }, watch: true, module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, //配置圖片的loader { test:/\.(jpg|png|jpeg|gif|svg)$/, //正則 loader:'url-loader?limit=4000&name=pic/[name].[ext]' //對應的loader,limit是限制圖片大小的,單位是bytes } ] } };
其中limit=4000表示小於4000bytes的圖片將直接以base64的形式內聯在代碼中,可以減少一次http請求;name=pic/[name].[ext]表示大於4000bytes的圖片將存入輸出路徑的pic/文件夾下,並且圖片命名格式不變。
然后執行npm run dev指令,然后我們用瀏覽器打開index.html文件看效果,兩個圖片都顯示出來了:

如果說我們的html文件中需要引入其他的html文件怎么辦呢,能不能也把其他的html通過webpack打包呢,也是可以的,接着看:
執行下面的指令:
npm i html-webpack-plugin --save-dev
然后在配置文件中配置loader
module.exports = { entry: { 'main': './main.js', //入口文件路徑 }, output: { 'filename': './bundle.js', //出口文件名稱和路徑 }, watch: true, module: { loaders: [ { test: /\.css$/, //正則表達式 loader: 'style-loader!css-loader' //注意寫法,!的作用是先執行后面的loader再執行前面的loader }, //配置圖片的loader { test:/\.(jpg|png|jpeg|gif|svg)$/, //正則 loader:'url-loader?limit=4000' //對應的loader }, //配置html文件的 { test:/\.less$/, loader:'style-loader!css-loader!less-loader' } ] } };
然后執行npm run dev指令就可以了,App.js中引入html文件也是用import,寫法和引入圖片的寫法相同,就不多演示了。
好,到目前位置,我們已經可以完成整個頁面中資源的加載和引用了,也就是完成了模塊化的開發,但是現在發現我們的目錄結構太亂了,我們來調整一下目錄結構,現在的目錄結構是這樣的:

調整一下目錄,我們在我們的項目目錄下創建一個src文件夾存放我們的main.js、App.js、index.css等文件,也就是我們開發時寫的內容文件,放到src文件夾中,然后我們的打包輸出的bundle.js先給他刪除,還有個vue.js文件,這是vue的功能文件,如果我們下載的這個vue.js文件的形式來使用的vue,那么這個vue.js文件一般也是放到src文件夾中的,如果我們是通過npm install vue的形式下載的,那么這個vue會在我們目錄中的那個node_modules文件夾中,圖片文件比如我們的meinv.jpg文件放到一個static文件夾下面的pic文件夾下面,然后調整后的目錄結構是下面這樣的:

圖片位置發生變化了,別忘了改我們的引入圖片的地方的代碼,我們在App.js這個模塊中引入的圖片,所以我們修改一下App.js文件,看代碼:
//引入圖片文件,將圖片內容作為一個變量引入,也就是給這個圖片對象起了個變量名,后面我們要通過這個變量名來使用這個圖片 import imgSrc from '../static/pic/meinv.jpg' //修改為這個路徑 // let imgSrc = require('../meinv.jpg'); 這樣寫也行 console.log(imgSrc); //定義一個組件 let App = { data(){ return{ img:imgSrc,//使用數據屬性來接收一下圖片對象,然后在template中使用一下 } }, template: ` <div> 我是App組件aaaaa <div> <img :src="img" alt=""> <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554823994028&di=db5686fc98db2a66a0b4738645922f09&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F170614%2F1311511X7-2.jpg" alt="" width="200" height="200"> </div> </div> `, }; //將組件拋出 export default App;
我們將來想將打包的那個bundle.js文件保存在一個叫做dist的文件夾下怎么搞呢,需要配置一下我們的webpack.dev.config.js和webpack.pro.config.js文件,看配置后的內容:
//引入nodejs的path模塊,解析路徑用的 const path = require('path'); module.exports = { entry: { 'main': './src/main.js', //main的路徑變了,別忘了修改 }, //出口配置也需要改一下 output: { // 'filename': './bundle.js', //出口文件名稱和路徑 //文件目錄調整之后,我們的出口文件配置也需要該一下,意思是將我們打包之后的那個js文件保存到一個我們設定的位置,看下面的寫法 path:path.resolve('./dist'),//相對轉絕對,這個path相當於python中的os.path功能模塊,它是nodejs的模塊,我們需要在上面引入一下這個模塊 filename:'bundle.js' //出口文件名稱,現在這個配置的意思是在我們的項目目錄下生成一個dist文件夾,並且將所有打包的模塊(也就是文件),以bundle.js文件名稱保存在這個dist文件夾中 }, watch: true, module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, { test:/\.(jpg|png|jpeg|gif|svg)$/, loader:'url-loader?limit=4000' }, { test:/\.less$/, loader:'style-loader!css-loader!less-loader' } ] } };
然后我們執行npm run dev指令,看效果,目錄結構中就多了dist文件夾,文件夾中就有了我們打包的bundle.js文件,還有我們配置的圖片,也就是這個dist文件夾是給我們將來上線的服務器用的,其中圖片是我們需要的,也就幫你一起打包到了這個dist文件夾里面,將來我們在服務器上做一下相關路徑配置就可以了,好,看看我們的目錄結構:

我們說這個dist文件夾是將來給我們的服務器的,那么我們html文件比如我們的index.html文件是不是也要發給服務器啊,當然啦,所以我們也希望我們的html文件資源也會自動打包到我們的dist文件夾下,那么此時我們就需要在進行一些配置,往下看:
先下載一個loader,執行下面的指令:
npm i html-webpack-plugin --save-dev #--save-dev就是 -D的意思
然后我們看一下package.json文件的變化:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", //多了一個它 "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^3.12.0" } }
執行完上面的指令,此時就需要用我們webpack中的另外一個核心內容,插件plugins。
插件plugins
loader 被用於轉換某些類型的模塊,而插件則可以用於執行范圍更廣的任務。插件的范圍包括,從打包優化和壓縮,一直到重新定義環境中的變量。插件接口功能極其強大,可以用來處理各種各樣的任務。
想要使用一個插件,你只需要 require() 它,然后把它添加到 plugins 數組中。多數插件可以通過選項(option)自定義。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 操作符來創建它的一個實例。
好,我們來配置一下我們的那兩個配置文件,webpack.dev.config.js和webpack.pro.config.js,內容如下:
const path = require('path');
//引入我們下載好的html文件的插件包
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
'main': './src/main.js',
},
output: {
path:path.resolve('./dist'),
filename:'bundle.js'
},
watch: true,
module: {
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test:/\.(jpg|png|jpeg|gif|svg)$/,
loader:'url-loader?limit=4000&name=pic/[name].[ext]' /
},
{
test:/\.less$/,
loader:'style-loader!css-loader!less-loader'
},
]
},
//配置插件
plugins:[
new HtmlWebpackPlugin({
//插件的執行運行與元素索引有關
template:'./index.html', //參照物,也就是將來打包的時候,打包哪個html文件啊,要給他一個參照
})
]
};
然后我們執行npm run dev指令,然后看我們的目錄結構

那么打開這個dist文件夾下的index.html文件看一下里面的內容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
<!-- 自動幫我們在index.html為你暗中添加了下面這個script標簽,並引入了bundle.js,所以以后我們打包html文件的時候,之前的html里面的用來引入bundle.js的script就可以刪除了,也就是上面這個我們之前寫的script標簽 -->
<script type="text/javascript" src="bundle.js"></script></body>
</html>
用瀏覽器打開這個index.html文件,看看效果,完全ok:

其實最后我們不管怎么開發,開發完成之后都要交給運維部署到線上,而我們給運維人員的東西就這個打包好的dist文件夾,里面有我們開發好的項目的所有資源。
其實我們要進行組件化開發,我們現在還沒有做完,我們繼續完善昂,首先我們src里面都是我們開發的內容,其中我們的App組件應該是.vue結尾的文件,然后進行單文件引入功能,如果想進行單文件引入,那么需要兩個工具,一個是vue-loader,一個是vue-template-compiler,那我們來下載一下,執行下面的指令: vue-loader就是用來解析我們的.vue結尾的文件的,vue-template-compiler是用來編譯.vue結尾的文件中的template模板的。
npm install vue-loader@14.1.1 vue-template-compiler@2.5.16 -D #注意這個vue-template-compiler要和你的vue版本一致
那么將來我們進行組件化開發,寫.vue結尾的文件組件時的結構是這樣的,看代碼:
//組件的模板結構 <template> <div> {{ text }} </div> </template> //組件的業務邏輯 <script> export default { data(){ return { text:'hello Single file' } } } </script> //組件的樣式 <style> body{ background-color: green; } </style>
好,上面的命令執行完之后,我們以后在寫組件就用.vue結尾的文件來寫,那么我們將我們的App.js文件改為App.vue文件,然后App.vue文件里面的內容就要按照上面的這種模板樣式來寫了。
然后我們還需要在我們的webpack.dev.config.js中配置一些內容:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
'main': './src/main.js',
},
output: {
path: path.resolve('./dist'),
filename: 'bundle.js'
},
watch: true,
module: {
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test: /\.(jpg|png|jpeg|gif|svg)$/,
loader: 'url-loader?limit=4000&name=pic/[name].[ext]'
},
{
test: /\.less$/,
loader: 'style-loader!css-loader!less-loader'
},
// 處理Vue文件,注意,就多了下面這個loader的配置
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
})
]
};
然后我們在App.vue文件中寫上下面的內容,看代碼:
//template是個標簽,我們下載的那個vue-template-compiler解析提供的,也是通過它來編譯這個標簽內容,而我們下載的安格vue-loader是解析vue文件的 <template> <div> 我是Jedan </div> </template> <script> export default { name:'App',//可以給這個組件起個名字 data(){ return{ } }, methods:{ }, //之前組件里面我們會寫一個template的屬性,現在不用了,因為我們下載的工具自動幫我們創建了一個template模板標簽,我們在上面直接使用就可以了 //template:``, } </script> <style> </style>
別忘了我們之前是在main.js里面引入的App.js,現在我們的文件叫App.vue了,你引入的地方也要改,看main.js的代碼:
import Vue from './vue.js' import App from './App.vue' //這個文件的名字別忘了改 import './index.css' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue實例對象</div> <App></App> </div> `, components:{ App, } });
然后執行一下npm run dev,然后通過瀏覽器打開index.html文件看效果:完美:

那么將來關於我們寫的所有組件,我們都可以放到src文件夾下的一個叫做components的文件夾中,看目錄結構

然后我們基於這種結構做一個單頁面應用:

首先,做單頁面應用會用到我們的vue-router,所以我們需要給我們的項目下載一下vue-router,我們也可以將我們之前用過的vue-router.js文件拿過來用,但是以后不建議下載文件來使用了,我們都直接通過指令給我們的項目來現在這些功能,包括vue.js,所以我們將vue.js文件刪了吧,我們通過npm下載,來執行下面指令,注意執行下載項目中需要的工具的時候,在終端下載的時候,我們要確保我們在自己項目目錄下執行的指令。
npm install vue@2.5.16 vue-router -S #下載vue(注意版本要和上面的vue-template-compiler版本一致)和vue-router,-S的是項目環境依賴,而之前我們下載的webpack會用到的一些工具,是開發環境依賴,沒有那些東西,你的項目照樣能跑起來,只不過不太方便,但是沒有vue和vue-router,你的單頁面應用是跑不起來的
執行上面的指令之后,我們看一下package.json文件的內容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.16", "webpack": "^3.12.0" }, "dependencies": { //多了它 "vue": "^2.6.10", "vue-router": "^3.0.3" } }
然后別忘了,我們之前main.js里面是導入vue.js文件來使用vue功能的,但是現在我們已經把vue.js刪了,並且是通過npm下載的vue,那么引用vue的時候,引用方式要改一改,看mian.js的內容:
// 不再是引用文件的形式引用vue了 // import Vue from './vue.js' import Vue from 'vue' //而是直接這樣引用,像引用內置模塊的形式 import App from './App.vue' import './index.css' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue實例對象</div> <App></App> </div> `, components:{ App, } });
既然我們要做單頁面應用使用main.js,那么我們把main.js的代碼改一改,引用vue-router,看代碼:
// 不再是引用文件的形式引用vue了 // import Vue from './vue.js' import Vue from 'vue' //而是直接這樣引用,像引用內置模塊的形式 import App from './App.vue' import './index.css' //引入vue-router import VueRouter from 'vue-router' //注意,引入我們下載好的包的時候,from后面一定是包的名字,前面import后面的名字你可以隨便起,盡量不要和你的包名字相同,現在的意思是我的vue-router功能包被引入進來了,並且起了個名字叫做VueRouter //聲明組件(引入組件) import Home from './components/Home/Home.vue' import Course from './components/Course/Course.vue' //別忘了,基於模塊化開發的時候,我們還要執行一下Vue.use(VueRouter) Vue.use(VueRouter); //接下來就可以寫我們的路由了,創建路由對象,別忘了將這個路由對象寫到vue實例中 const router = new VueRouter({ //配置路由信息 routes:[ { path:'/', name:'Home', component:Home //對應Home組件,而我們的Home組件在src文件夾下的components文件夾的Home文件夾下面,所以我們在上面就需要引入一下我們的Home組件了 }, { path:'/course', name:'Course', component:Course //對應Home組件,而我們的Home組件在src文件夾下的components文件夾的Home文件夾下面,所以我們在上面就需要引入一下我們的Home組件了 } ] }); new Vue({ el:'#app', router,//給vue實例綁定路由對象 data(){ return{ } }, //使用render方法來渲染,因為后面咱們要下載一個個webpack-dev-server,高級一些的webpack打包工具,但是不太支持下面原來的寫法,所以我換成了render方法 render:c=>c(App), // template: // ` // <div> // <div>我是Vue實例對象</div> // <App></App> // </div> // `, // components:{ // App, // } });
然后我們的App.vue里面就可以使用我們的router-link和router-view了,看代碼:
//template是個標簽,我們下載的那個vue-template-compiler解析提供的,也是通過它來編譯這個標簽內容,而我們下載的安格vue-loader是解析vue文件的 <template> <div> 我是Jedan <router-link :to="{name:'Home'}">首頁</router-link> <router-link :to="{name:'Course'}">課程頁</router-link> <router-view></router-view> </div> </template> <script> export default { name:'App',//可以給這個組件起個名字 data(){ return{ } }, methods:{ }, //之前組件里面我們會寫一個template的屬性,現在不用了,因為我們下載的工具自動幫我們創建了一個template模板標簽,我們在上面直接使用就可以了 //template:``, } </script> <style> </style>
然后我們寫一下我們的Home組件和Course組件:
Home.vue代碼如下:
<template>
<div>
這里是首頁
</div>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
Course.vue代碼如下:
<template>
<div>
這里是課程頁
</div>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
組件都寫好了之后,我們再學一個新東西,叫做webpack-dev-server,比webpack更高級一些的打包工具,是基於服務器的,他能夠在前端起服務器,來服務端的形式打開我們的vue項目,這個工具可以自動打開瀏覽器、熱更新、自動刷新等功能,下面我們來下載一下,執行指令:
npm install webpack-dev-server --save-dev
這個webpack-dev-server模塊的常用配置參數如下:
常用配置參數 --open 自動打開瀏覽器 --hot 熱更新 ,不在刷新的情況下替換 css樣式 --inline 自動刷新 --port 9999 指定端口 --process 顯示編譯進度
下載好了以后,我們來使用一下,還記得我們之前在package.json文件中如何配置我們的webpack指令的嗎,就是執行那個npm run dev,就會執行webpack打包的那個地方,我們需要改一改寫法了,我下面只修改一下開發環境的那個webpack指令,看packag.json的內容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js", //就是這個地方,之前是用的webpack --config ./webpack.dev.config.js,現在用我們的webpack-dev-server了,使用webpack-dev-server,不會幫你生成dist文件夾,因為你是開發環境,webpack會幫你生成 "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.16", "webpack": "^3.12.0", "webpack-dev-server": "^2.11.5" }, "dependencies": { "vue": "^2.5.16", "vue-router": "^3.0.3" } }
好,配置好以后,我們把之前webpack打包出來的那個dist文件夾刪除,然后再執行npm run dev,就會通過webpack-dev-server來打包,並且它自動會幫我們打開頁面

然后你就看到了頁面效果,看瀏覽器的地址欄,是一個服務端的形式:

然后你在組件的模板中隨便改寫內容,頁面會自動刷新,不需要你手動刷新了。
好,到這里,我們的DIY腳手架就算是搭建完成了,那么我們說一下我們的目錄結構,其實我們將來用腳手架,dist文件夾是沒有的,我們自己開發完之后,通過打包才生成的,好了,感受了一下搭建腳手架的過程,DIY腳手架大家不用會,理解過程就行了,我們還是學習怎么用人家vue給我們提供的腳手架吧,繼續下面的學習~~
首先我們對比一下vue-cli2x版本和vue-cli3x版本的一些區別,現在2x版本的:
安裝
npm install -g vue-cli
用法
$ vue init < template-name模塊名 > < project-name項目名 >
創建一個例子:
$ vue init webpack my-project
目前除了webpack之外,其他的一些可用模塊:
目前可用的模塊包括: - [webpack](https://github.com/vuejs-templates/webpack) - 一個功能齊全的Webpack + vue-loader設置,具有熱重載,linting,測試和css提取功能。 - [webpack-simple](https://github.com/vuejs-templates/webpack-simple) - 一個簡單的Webpack + vue-loader設置,用於快速原型設計。
目前主要用的上面兩個,webpack-simple簡單一些,webpack功能更加齊全
- [browserify](https://github.com/vuejs-templates/browserify) -全功能Browserify + vueify設置用熱重裝載,linting&單元測試。 - browserify [-simple](https://github.com/vuejs-templates/browserify-simple) - 一個簡單的Browserify + vueify設置,用於快速原型設計。 - [pwa](https://github.com/vuejs-templates/pwa) - 基於webpack模板的vue-cli的PWA模板,主要應用於移動端的開發 - [simple](https://github.com/vuejs-templates/simple) - 單個HTML文件中最簡單的Vue設置
然后我們下用webpack-simple來創建一個項目,在你的目錄下執行下面的指令:

然后我們的目錄下就有了這個項目的文件,我們看一下目錄結構:

然后說幾個目錄下的其他文件是干什么的,作為了解吧:

那么node_modules這些依賴包怎么下載呢,我們看一下我們都少了哪些依賴包和工具啊,看一下自動生成的package-lock.json文件:

好,我們切換到我們的項目目錄下,然后執行一個npm install指令,就會自動幫我們下載項目的依賴了:

然后執行指令npm run dev指令,你就會發現,頁面自動打開了:

頁面效果:

好,一個vue的項目就成型了,就像我們學python的時候,通過django創建一個項目,直接一運行,看到django給你們提供的首頁似的那個感覺。
然后我們看一下執行npm run dev指令后的一個項目執行順序:

如果將來我們項目寫完了,想打包發給上線人員,那么我們執行npm run build指令,就會生成打包好的dist文件夾,里面有咱們的圖片文件,js文件等,如果你安裝了咱們說的那個html文件打包插件,那么這個dist文件夾里面也有咱們的html文件。

然后看一下webpack.config.js里面的內容:
var path = require('path') //引入路徑解析墓模塊 var webpack = require('webpack') //引入webpack module.exports = { entry: './src/main.js', //webpack打包時的入口文件,將來我們就先從這個文件開始玩咱們的項目 output: { //出口文件配置 path: path.resolve(__dirname, './dist'), //__dirname是項目的絕對路徑,resolve將他倆拼接了一下 publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ //可以寫成loaders { test: /\.css$/, //打包css文件用的 use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.vue$/, //打包vue文件用的 loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, //打包js文件用的 loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, //打包圖片文件用的 loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { //別名 'vue$': 'vue/dist/vue.esm.js' //esm是esmodule }, extensions: ['*', '.js', '.vue', '.json'] //擴展,凡是后綴名有他們幾個的文件,后綴名可以忽略不寫 }, //在下面的那些配置咱就不多看了 devServer: { historyApiFallback: true, noInfo: true, overlay: true }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
項目的入口是main.js文件,打開看看,你會發現你很熟悉:
import Vue from 'vue' //引入vue import App from './App.vue' //引入App組件,也是我們創建項目時自動給你生成好的,有import,那么App.vue文件中肯定有export new Vue({ el: '#app', render: h => h(App) //掛載,使用APP組件,和template+components一樣,但是webpack-dev-server支持這個方法,不支持template, })
然后看一下App.vue組件中的內容:就是咱們創建完項目,下載完依賴,然后執行npm run dev指令之后你看到的那個頁面里面的內容:
<template>//還記得.vue結尾的文件的模板嗎,template script style三個標簽,template是組件中頁面渲染的標簽,script標簽中拋出組件,style標簽中寫css樣式,以后,我們直接把下面這些內容清空,也我們的單頁面應用就可以了
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
<li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
<li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</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;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
好,接下來我們就要玩我們的單頁面應用了,還記得單頁面應用需要什么嗎,vue-router,首先下載,執行下面的指令:
npm i vue-router -S 注意,別用-g,而是用-S,因為-g是下載到全局了,全局的意思就是我們下載安裝vue的咱們那個電腦目錄里面了,而-S是下載到我們當前項目的依賴中了,不一樣的昂
然后我們需要寫我們路由配置了,在哪里配置呢,還記得嗎,就是我們的main.js文件,並且之前我們寫路由配置信息的時候,都是寫在了這個文件里面,那么隨着我們項目越來越大,你會發現路由信息會越來越多,那么為了方便管理,我們應該將路由信息單獨的放到一個文件里面去,並且,我們路由肯定都對應着組件,也就是說將來的組件也會越來越多,所以組件我們也單獨的存放起來,並且我們以后開發,都只關注src文件夾里面的內容,所以路由和組件都放到src文件夾里面,看下面的目錄結構:

目錄結構搞好了,我們就開始開發了,看代碼:
main.js代碼如下:
import Vue from 'vue' import App from './App.vue' //導入router路由配置信息,因為我們要在vue實例對象里面掛載一下,還記得嗎 // import router from './router/router-config' //還記得webpack.config.js里面有個配置說,凡是.js等結尾的文件,可以不寫后綴名嗎,寫不寫都可以 import router from './router/router-config.js' //有import,就肯定要在router-config.js文件中有個export拋出router對象 new Vue({ el: '#app', router,//掛載router對象 render: h => h(App) });
router-config.js路由配置信息代碼如下:
//引用vue,因為vue-router依賴於vue import Vue from 'vue' import VueRouter from 'vue-router' //引入對應的組件 import Home from '../components/Home/Home' import Course from '../components/Course/Course' //給Vue加載一下VueRouter,其實類似於繼承一下 Vue.use(VueRouter); const router = new VueRouter({ routes:[ { path:'/home', name:'Home', component:Home, }, { path:'/course', name:'Course', component:Course, } ] }); export default router;
接下來是對應組件,Home.vue組件代碼如下:
<template> <div> 這是首頁 </div> </template> <script> export default { //拋出組件,供別的地方引用 name:'Home', // 這個name在做全局組件的時候比較有用,還記得全局組件怎么創建嗎?vue.component('Home',{}),第一個參數就是組件名稱,其他組件使用這個全局組建的時候就靠的這個組件名稱,而我們這里寫的name,就可以起到這個作用,將來別人掛載你的組件的時候,就用它 data(){ return{ } } } </script> <style> </style>
Course.vue組件代碼如下:
<template> <div> 這是課程頁 </div> </template> <script> export default { name:'Course', data(){ return{ } } } </script> <style> </style>
然后是App.vue組件中要引入這兩個組件了,看App.vue文件的代碼如下:
<template>
<div id="app">
<!-- 通過路由對應路徑,自動生成a標簽,href屬性指向對應的路徑,還記得嗎? -->
<router-link :to="{name:'Home'}">首頁</router-link>
<router-link :to="{name:'Course'}">課程頁</router-link>
<!-- 組件出口 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
msg: 'Jaden'
}
}
}
</script>
<style>
</style>
然后執行npm run dev,直接就看到了我們頁面效果,點擊首頁就看到了首頁內容,點擊課程頁,就看到了課程頁內容:

齊活,我們的項目就寫完了,哈哈
接下來我們玩一下CSS樣式,學一個scoped屬性。
我給App組件還有Home和Course組件都加了個h2標簽,然后App組件的style標簽中給h2標簽設置了樣式,大家看圖:

看效果:

由於App組件算是我們整個頁面的大組件,全局組件,所以如果直接按照上面的方式來設置樣式,那么App組件中嵌套的其他組件也會應用上這個樣式,並且如果我們在其他組件里面給h2標簽設置了其他的樣式,那么只要點擊加載過這個組件,那么全局的所有的組件的h2標簽的樣式就會編程這個樣式,所以很不好控制,看圖:

看效果,首先剛開始的時候,我們頁面加載了App組件,效果是這樣的:

但是只要點擊了Course組件,看效果:

你會發現,這樣很難控制我們每個組件的自己的樣式,這怎么搞呢,style標簽的scoped屬性,注入的意思。

看效果:

這就是scoped屬性的作用。
以上是我們使用webpack-simple模塊創建的項目的玩法,但實際工作中我們用的一般都是webpack模塊,復雜一些的模塊,功能也多,而且文件夾都給咱們分好了。但是學了webpack-simple之后,webpack對你來說也簡單了,接下來我們學一下wepack模塊創建項目的玩法。
首先我們通過webpack來創建一個項目,在IDE終端執行下面的指令
vue init webpack 05-webpack-project(項目名稱)
然后終端會給咱們一些提示輸入信息的選項,看下圖:

除了上面這些提示,還有一些提示,接着看:

這里項目的依賴,我選擇的是npm下載,回車,然后等一會就下載完了,下載完之后還有提示:

然后按照提示,我們cd到我們的項目目錄下,執行npm run dev指令,就啟動我們的項目了,在終端你會看到這個:

然后拿着這個ip地址和端口,到我們的瀏覽器上訪問一下,就看到我們的頁面了:

下面我們看一下新生成的項目的目錄結構:

然后我們執行npm run dev會執行什么呢,還記得嗎,是不是找package.json里面的scripts屬性里面的dev對應的那個指令啊:

然后看一下這個webpack.dev.conf.js文件:

然后看一下這個webpack.base.conf.js文件,就看到了我們熟悉的內容:

那么這些配置文件咱們其實都不用管,我們只需要專注的玩我們src文件夾里面的內容就可以了,開發者只需要關心src這個文件夾,那么我們先從入口文件main.js看看里面的內容:

然后你會發現,其實這個main.js文件你都不用管了,我們只需要在我們的src文件夾里面的App.vue組件寫一寫,然后把components文件下的組件寫一寫、分一分,然后把router文件夾下的index.js路由配置信息改一改,就完事兒了。下面我就簡單的寫一個單頁面應用,大家感受一下吧,先看目錄結構:

然后直接上代碼:
main.js文件內容如下:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
router文件夾下的index.js路由配置信息內容如下:
import Vue from 'vue' import Router from 'vue-router' // import HelloWorld from '@/components/HelloWorld' //@還記得嗎,表示src文件夾的根路徑 //引入組件 import Course from '@/components/Course/Course' import Home from '@/components/Home/Home' // console.log(Course); //給Vue添加vue-router功能,使用別人提供的功能都要用Vue來use一下 Vue.use(Router) //創建路由對象 export default new Router({ mode:'history', //去掉瀏覽器地址欄的#號 //配置路由信息 routes: [ { path: '/', // redirect:'Home' //直接跳轉Home名字對應的path路徑上 redirect:'/home' }, { path: '/home', name: 'Home', component: Home }, { path: '/course', name: 'Course', component: Course } ] })
App.vue組件文件的內容如下:
<template>
<div id="app">
<router-link :to="{name:'Home'}">首頁</router-link>
<!--<router-link :to="{name:'Home',query:{user_id:2}}">首頁</router-link>-->
<!--query:{user_id:2} 是query參數,拼接到路徑上是這樣的/home?user_id=2,這也是動態路由的一種,之前我們說了一個param參數,這個param參數是/home/2,拼接在路徑上的-->
<router-link :to="{name:'Course'}">免費課程</router-link>
<!-- 組件出口 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return{
}
},
}
</script>
<style>
</style>
Home.vue組件內容如下:
<template> <div> 這是Home頁面 </div> </template> <script> export default { name: 'Home', data(){ return{ } }, } </script> <style> </style>
Course.vue組件內容如下:
<template> <div> 這是Course頁面 </div> </template> <script> export default { name: 'Course', data(){ return{ } }, } </script> <style> </style>
把這幾個文件寫好,那么我們執行npm run dev,就啟動我們的項目了,按照ip地址和端口的提示,我們在瀏覽器上訪問一下這個地址,就看到我們的頁面應用了:

效果:

非常好,單頁面應用我們就寫完了,剩下的就是完善網站的功能和美化頁面效果了。
簡單總結一下:
1. webpack:
entry:整個項目的程序入口(一般是main.js或index.js)
output:輸出的出口
loader:加載器、轉化器和對es6代碼的解析(這個解析用的是babel編譯器,大家可以去看看babel),有些低版本的瀏覽器不能識別es6的代碼,為了做這種兼容性,也就是為了讓低版本的瀏覽器能夠識別es6的代碼,前端在webpack一般用這個babel loader去處理,去解析,看babel官網。

再比如還有什么css-loader解析css文件和style-loader將css代碼添加一個style標簽,插入到header標簽中,並且支持模塊化。還有一些圖片引入等用的是url-loader,還有很多其他的loader,他的作用就是對我們的靜態文件資源做支持的。
plugins:插件,比如那個html-webpack-plugin,打包html文件用的,還有一些插件做代碼壓縮的(那個丑陋js插件),還有分割js文件的插件,比如js文件比較大的時候,會一部分一部分的加載,還有我們知道咱們現在是組件化開發,每個組件都有自己的js文件,而目前為止我們都是將所有的js文件全部打包了,每次使用都是將所有的js代碼全部加載到瀏覽器上,但是如果做個非常大型的項目,我們應該考慮,點擊一個頁面,這個頁面對應的那些組件的js代碼才加載到頁面上,也就是將js代碼或者js文件分開,這個就稍微麻煩一些了,大家可以去研究一下webpack。前端做的工作還有就是項目上線之前,對整個項目進行優化。webpack內容很多,如果想做一個專業的前端,需要好好學習和關注webpack,還有其他的前端社區,最新的技術都是需要學習的,不光前端如此,后端也是如此,機會是給有准備的人的。
2.使用vue-cli
a.電腦上(linux,windows等)需要安裝nodejs,使用npm包管理器
b.npm下載腳手架,如果你下載的是最新的3.x版本的,那么別忘了拉一下2.x版本的橋接工具,為了兼容2.x版本,目前公司里用的比較多的還是2.x版本的。3.x版本和2.x版本創建項目的命令也是不同的,大家自行看看吧。
安裝3.x版本:npm install -g @vue/cli ,拉取2.x版本的:npm install -g @vue/cli-init
創建項目:vue init webpack my-project
執行指令的時候先看清楚當前終端的根目錄,如果不是我們的項目目錄,一定要先切換到項目目錄下,然后執行npm install 來下載項目的所有依賴包
執行項目:npm run dev(當然dev這個名字,自己可以配置,在package.json文件中的scripts屬性中改)
3. .vue組件文件,結合我們學的基礎,怎么玩,看代碼:
<template>
<!--當前組件的結構-->
<div>
<h2>{{ msg }}</h2> <!-- 模板語法,渲染組件的數據屬性 -->
<!-- 指令系統 v-html、v-for(別忘了:key,不然我們如果更改了組件中的一些內容,重新排版頁面的時候,可能會出現問題,出現混亂,改的數據跑到了別的地方), -->
<!--<span(或者組件標簽) v-for="(value,index) in nameList" :key="index或者value.id"></span>-->
<!-- v-bind(:), v-on(@),v-model雙向數據綁定 -->
<!-- 使用element-ui組件,作為我們組件的子組件,里面的數據通過父子組件,平行組件傳值 -->
</div>
</template>
<script>
//當前組件的業務邏輯
export default {
name: 'Test',
data(){
return{
msg:'Jaden',
nameList:[]
}
},
methods:{
},
//計算屬性
computed:{
},
//還有各種鈎子函數
created(){
},
watch:{},
//過濾器
filter:{
myTime:function (val) {
}
}
}
</script>
<style scoped>
/*當前組件的樣式*/
</style>
補充一點,怎們將我們的組件做成一個全局組件呢?那就想一下全局組件怎么創建,是不是vue.component('組件名',{}),好,要使用vue,那么哪個文件引用的vue,是不是我們的main.js啊,所以我們在main.js里面就可以將我們的組件做成全局組件,大概的寫法看下面的代碼,main.js的:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false //將我們之前的Home組件做成一個全局組件 // import HomeContent from './components/Home/Home' // Vue.component('HomeContent.name',HomeContent); /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
element-ui是餓了么開發的一套前端框架,類似於bootstrap,但是他是協同vue進行開發的,它提供好了很多vue能夠直接下載使用的組件,組件里面封裝好了html、css、js等代碼,拿來就能用,就能得到對應的效果。
好,我們進入官網看一下http://element-cn.eleme.io/#/zh-CN/,其中我們就看組件的部分:

首先下載安裝,推薦使用 npm 的方式安裝,它能更好地和 webpack 打包工具配合使用。執行下面的指令來安裝:
npm i element-ui -S
然后怎么玩呢,看下圖:

那么好,知道了步驟,我們就來項目里面搞一搞,首先終端執行上面的下載安裝命令,安裝完成后我們去配置我們的main.js,看代碼:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' //引入element-ui import ElementUI from 'element-ui'; //引入element-ui的css樣式,這個需要單獨引入 import 'element-ui/lib/theme-chalk/index.css'; //給Vue添加一下這個工具 Vue.use(ElementUI); Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
然后使用一下element-ui的組件:

然后在我們的App.vue組件中使用一下,看App.vue文件的代碼:
<template>
<div id="app">
<el-container> <!-- 這是個組件,全局組件,是當前App組件的子組件,這些element-ui里面的這些組件肯定都是些全局組件,我們引入了element-ui,並且vue.use()了一下,將像vue.component(element-ui組件,{}),這些子組件里面的數據就是我們父子組件傳值傳進去的,是說對不對 -->
<el-header> <!-- 將組件入口寫到了這header里面 -->
<router-link :to="{name:'Home'}">首頁</router-link>
<router-link :to="{name:'Course'}">免費課程</router-link>
</el-header>
<el-main>
<!-- 組件出口寫到了el-main里面 -->
<router-view></router-view>
</el-main>
</el-container>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {}
},
}
</script>
<style>
</style>
然后執行npm run dev指令,啟動我們的項目,然后在頁面上看效果:
之前的頁面效果是這樣的:

加上element-ui的布局容器之后,效果是這樣的:

好,那么我們再添加一些css樣式:

我們在static文件夾下創建一個文件夾存放css樣式,先創建一個全局的css樣式文件夾,里面創建一個index.css文件,來寫全局的一些css樣式,看目錄結構:

然后我們在main.js里面引入一下這個全局css樣式,看main.js的內容:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' //引入element-ui import ElementUI from 'element-ui'; //引入element-ui的css樣式,這個需要單獨引入 import 'element-ui/lib/theme-chalk/index.css'; //給Vue添加一下這個工具 Vue.use(ElementUI);
//引入我們自己創建的css樣式文件 import '../static/global/index.css' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
看頁面效果:這樣我們的css樣式就有了。

再舉一個例子吧,比如說來一個時間選擇器:

然后比如說我們在Home組件中使用一下,看一下Home.vue文件內容:
<template> <div> 這是Home頁面 <el-time-select v-model="value1" :picker-options="{ start: '08:30', step: '00:15', end: '18:30' }" placeholder="選擇時間"> </el-time-select> </div> </template> <script> export default { name: 'Home', data() { return { value1:'', //時間選擇器,這里別忘了return一個value1來對應上面的v-model } }, } </script> <style> </style>
效果就出來了:

然后大家自己過一下element-ui都給咱們提供了哪些組件就行啦,以后直接就用。
我們再說一個組件怎么用吧,就是element-ui提供的Carousel走馬燈(輪播圖)

看這個走馬燈怎么玩:

好,到這里我們就說的差不多了,大家完成一個內容吧:

然后主要寫免費課的那個組件,也就是點擊免費課的那個頁面:

行,完成這些內容吧!
console.log(person,num);
