Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
早的前端,沒有模塊加載規范,只能在HTML中通過<script>來引入js文件,同時無法區分函數來源于哪個js文件,而且要用過多全局變量。而隨著前端工程復雜度的提升,使用這種方式已經無法滿足日益增長的開發需求,js的模塊化應運而生。
CommonJS 是屬于 Node.js 的模塊化方案,最早是叫 ServerJS,隨著 Node.js 的火爆發展而成名的。Module1.0 規范在 Node.js 上實踐的很好。
而 JavaScript 在當時(ES6 Modules 規范還未誕生)是沒有模塊化方案的,所以又更名從 CommonJS,想要統一服務端與客戶端的模塊加載方案。
但是,require 函數是同步的,在瀏覽器端由于網絡的瓶頸而不適用。于是,AMD 和 CMD 的規范相繼涌現,和 CommonJS 一起服務于 JavaScript 模塊化。
而正是規則的不統一,這也是目前兼容方案 UMD 會出現的原因。
不過AMD 和 CMD 的瀏覽器端模塊化,有很明顯的問題:
現如今,當打包這一環節被引入了前端工程化,CommonJS 以與服務端可以類庫共用和 NPM(Node Package Manager) 這個后臺的優勢,成為了 es5 JavaScript 模塊化的首選
CommonJS 是一個旨在構建涵蓋web服務器端、桌面應用、命令行app和瀏覽器JS的JS生態系統。
CommonJS 的標準符合 module1.1 規范,暴露給使用者的有三個全局變量:
本文講的是如何模擬一個 $require 函數,先來捋一捋 require 函數的主邏輯
require
require是一個基于AMD規范實現的函數,它區別于傳統的CommonJS require規范。因為它能夠異步地加載動態的依賴,所以,我們對基于require的回調方式有了更多的需求。
API Specification
局部require vs 全局require。局部require可以被解析成一個符合AMD工廠函數規范的require函數。
define(['require'], function (require) { //the require in here is a local require. }); define(function (require, exports, module) { //the require in here is a local require. });
局部require也支持其他標準實現的API。
全局require函數作用于全局,和define()類似。 全局require和局部require有著相同的行為,包含以下特征:
實際中,我們經常會遇到一些阻塞模塊加載的依賴,如果交互次數很多,需要大量的模塊加載,應該采用全局依賴的形式去加載頂層模塊。
require(String)
基于以下規范CommonJS Modules 1.1.1 require.根據參數,同步地返回模塊ID所代表的模塊。
如果模塊沒有加載或者執行完成,就會拋出錯誤。特別需要指出的是,在同步加載的回調中,如果模塊沒有加載完成,禁止動態的獲取模塊,否則,就會拋出異常。
使用define()定義模塊時,依賴項中可以找到一個AMD模塊:
define(function (require) { var a=require('a'); });
工廠方法可以被解析成require('')的調用形式(例如,使用語法解析器或者使用Function.prototype.toString()或者正則表達式)去找到依賴,加載并且執行依賴,然后執行工廠方法內部代碼,通過這樣的方式,就可以獲取到模塊。
require(Array, Function)
參數Array是一個由模塊ID組成的數組。當模塊ID所以代表的模塊加載完成且可用時,回調函數Function才開始執行,并且只被執行一次。各個模塊按照依賴數組中的位置順序以參數的形式傳入到Function里。
例如:
define(function (require) { require(['a', 'b'], function (a, b) { //modules a and b are now available for use. }); });
require.toUrl(String)
將形如**[module ID] + '.extension'**這種字符形式轉化成URL路徑。
require.toUrl()方法采用通用的模塊ID路徑轉化規則,將模塊ID字符解析成URL路徑.但它不支持以".js"這種擴展形式。所以,我們必須將'.extension'添加到了解析路徑里。
例如:
//cart.js contents: define (function(require) { // 模塊ID名 './templates/a' // 擴展名 '.html' // 模板路徑大致以這樣的形式結尾 'modules/cart/templates/a.html' var templatePath=require.toUrl('./templates/a.html'); });
以上是require的具體用法展示。
根據這個邏輯,我們先寫一個 main 函數,以及定義一些需要的接口
至此已經有一個不可執行的 require 函數了,邏輯中會依次判斷是否有緩存,是否核心模塊,加載相應文件以及加載執行模塊并緩存。
可以看到沒有具體實現,只有接口被定義出來,這種編碼方式,同樣可以借鑒到在其他的開發需求中:
這一步驟,主要是對上述過程需要的接口進行實現。
思想上是一個分而治之的思想,實現一個很復雜的東西比較困難,但是實現具體的功能要求,且在一定輸入輸出的限制下,每個人都能輕易的寫出符合需求的算法,并進行調優。
數據結構與工具函數類
棧 -- 存儲當前模塊的所在目錄
function Stack(...args) { this._stack=new Array(...args); } Stack.prototype={ top: function () { return this._stack .slice(-1)[0] }, push: function (...args) { this._stack.push(...args) }, pop: function () { this._stack.pop() }, constructor: Stack }
這個棧的作用是存放當前模塊的所在目錄,用于模塊內 require 函數傳入相對路徑時,解析成絕對路徑
獲取文件所在目錄
function getParent(pathname) { return path.parse(pathname).dir }
檢測模塊類型與定位包的位置
這個函數要做下面的事情
以下部分,是 getModuleLocation 自身需要實現的接口,往往是開發過程中自行提煉的,其他模塊不通用
定位引用的真實路徑
這個函數是 CommonJS 模塊化的核心體現,理解這個函數,對 module 和 exports 的實際使用也會有幫助
以為到這里就完成了嗎,不,至少還有一些問題需要考慮:
鏈接文章
https://github.com/kaola-fed/blog/issues/16
https://github.com/amdjs/amdjs-api/wiki/require-(中文版)
文講解怎樣用 Node.js 高效地從 Web 爬取數據。
前提條件
本文主要針對具有一定 JavaScript 經驗的程序員。如果你對 Web 抓取有深刻的了解,但對 JavaScript 并不熟悉,那么本文仍然能夠對你有所幫助。
你將學到
通過本文你將學到:
了解 Node.js
Javascript 是一種簡單的現代編程語言,最初是為了向瀏覽器中的網頁添加動態效果。當加載網站后,Javascript 代碼由瀏覽器的 Javascript 引擎運行。為了使 Javascript 與你的瀏覽器進行交互,瀏覽器還提供了運行時環境(document、window等)。
這意味著 Javascript 不能直接與計算機資源交互或對其進行操作。例如在 Web 服務器中,服務器必須能夠與文件系統進行交互,這樣才能讀寫文件。
Node.js 使 Javascript 不僅能夠運行在客戶端,而且還可以運行在服務器端。為了做到這一點,其創始人 Ryan Dahl 選擇了Google Chrome 瀏覽器的 v8 Javascript Engine,并將其嵌入到用 C++ 開發的 Node 程序中。所以 Node.js 是一個運行時環境,它允許 Javascript 代碼也能在服務器上運行。
與其他語言(例如 C 或 C++)通過多個線程來處理并發性相反,Node.js 利用單個主線程并并在事件循環的幫助下以非阻塞方式執行任務。
要創建一個簡單的 Web 服務器非常簡單,如下所示:
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, () => {
console.log(`Server running at PORT:${port}/`);
});
如果你已安裝了 Node.js,可以試著運行上面的代碼。Node.js 非常適合 I/O 密集型程序。
HTTP 客戶端:訪問 Web
HTTP 客戶端是能夠將請求發送到服務器,然后接收服務器響應的工具。下面提到的所有工具底的層都是用 HTTP 客戶端來訪問你要抓取的網站。
Request
Request 是 Javascript 生態中使用最廣泛的 HTTP 客戶端之一,但是 Request 庫的作者已正式聲明棄用了。不過這并不意味著它不可用了,相當多的庫仍在使用它,并且非常好用。用 Request 發出 HTTP 請求是非常簡單的:
const request = require('request')
request('https://www.reddit.com/r/programming.json', function ( error,
response,
body) {
console.error('error:', error)
console.log('body:', body)
})
你可以在 Github 上找到 Request 庫,安裝它非常簡單。你還可以在 https://github.com/request/request/issues/3142 找到棄用通知及其含義。
Axios
Axios 是基于 promise 的 HTTP 客戶端,可在瀏覽器和 Node.js 中運行。如果你用 Typescript,那么 axios 會為你覆蓋內置類型。通過 Axios 發起 HTTP 請求非常簡單,默認情況下它帶有 Promise 支持,而不是在 Request 中去使用回調:
const axios = require('axios')
axios
.get('https://www.reddit.com/r/programming.json')
.then((response) => {
console.log(response)
})
.catch((error) => {
console.error(error)
});
如果你喜歡 Promises API 的 async/await 語法糖,那么你也可以用,但是由于頂級 await 仍處于 stage 3 ,所以我們只好先用異步函數來代替:
async function getForum() {
try {
const response = await axios.get(
'https://www.reddit.com/r/programming.json'
)
console.log(response)
} catch (error) {
console.error(error)
}
}
你所要做的就是調用 getForum!可以在 https://github.com/axios/axios 上找到Axios庫。
Superagent
與 Axios 一樣,Superagent 是另一個強大的 HTTP 客戶端,它支持 Promise 和 async/await 語法糖。它具有像 Axios 這樣相當簡單的 API,但是 Superagent 由于存在更多的依賴關系并且不那么流行。
用 promise、async/await 或回調向 Superagent 發出HTTP請求看起來像這樣:
const superagent = require("superagent")
const forumURL = "https://www.reddit.com/r/programming.json"
// callbacks
superagent
.get(forumURL)
.end((error, response) => {
console.log(response)
})
// promises
superagent
.get(forumURL)
.then((response) => {
console.log(response)
})
.catch((error) => {
console.error(error)
})
// promises with async/await
async function getForum() {
try {
const response = await superagent.get(forumURL)
console.log(response)
} catch (error) {
console.error(error)
}
}
可以在 https://github.com/visionmedia/superagent 找到 Superagent。
正則表達式:艱難的路
在沒有任何依賴性的情況下,最簡單的進行網絡抓取的方法是,使用 HTTP 客戶端查詢網頁時,在收到的 HTML 字符串上使用一堆正則表達式。正則表達式不那么靈活,而且很多專業人士和業余愛好者都難以編寫正確的正則表達式。
讓我們試一試,假設其中有一個帶有用戶名的標簽,我們需要該用戶名,這類似于你依賴正則表達式時必須執行的操作
const htmlString = '<label>Username: John Doe</label>'
const result = htmlString.match(/<label>(.+)<\/label>/)
console.log(result[1], result[1].split(": ")[1])
// Username: John Doe, John Doe
在 Javascript 中,match() 通常返回一個數組,該數組包含與正則表達式匹配的所有內容。第二個元素(在索引1中)將找到我們想要的 <label> 標記的 textContent 或 innerHTML。但是結果中包含一些不需要的文本( “Username: “),必須將其刪除。
如你所見,對于一個非常簡單的用例,步驟和要做的工作都很多。這就是為什么應該依賴 HTML 解析器的原因,我們將在后面討論。
Cheerio:用于遍歷 DOM 的核心 JQuery
Cheerio 是一個高效輕便的庫,它使你可以在服務器端使用 JQuery 的豐富而強大的 API。如果你以前用過 JQuery,那么將會對 Cheerio 感到很熟悉,它消除了 DOM 所有不一致和與瀏覽器相關的功能,并公開了一種有效的 API 來解析和操作 DOM。
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')
$('h2.title').text('Hello there!')
$('h2').addClass('welcome')
$.html()
// <h2 class="title welcome">Hello there!</h2>
如你所見,Cheerio 與 JQuery 用起來非常相似。
但是,盡管它的工作方式不同于網絡瀏覽器,也就這意味著它不能:
因此,如果你嘗試爬取的網站或 Web 應用是嚴重依賴 Javascript 的(例如“單頁應用”),那么 Cheerio 并不是最佳選擇,你可能不得不依賴稍后討論的其他選項。
為了展示 Cheerio 的強大功能,我們將嘗試在 Reddit 中抓取 r/programming 論壇,嘗試獲取帖子名稱列表。
首先,通過運行以下命令來安裝 Cheerio 和 axios:npm install cheerio axios。
然后創建一個名為 crawler.js 的新文件,并復制粘貼以下代碼:
const axios = require('axios');
const cheerio = require('cheerio');
const getPostTitles = async () => {
try {
const { data } = await axios.get(
'https://old.reddit.com/r/programming/'
);
const $ = cheerio.load(data);
const postTitles = [];
$('div > p.title > a').each((_idx, el) => {
const postTitle = $(el).text()
postTitles.push(postTitle)
});
return postTitles;
} catch (error) {
throw error;
}
};
getPostTitles()
.then((postTitles) => console.log(postTitles));
getPostTitles() 是一個異步函數,將對舊的 reddit 的 r/programming 論壇進行爬取。首先,用帶有 axios HTTP 客戶端庫的簡單 HTTP GET 請求獲取網站的 HTML,然后用 cheerio.load() 函數將 html 數據輸入到 Cheerio 中。
然后在瀏覽器的 Dev Tools 幫助下,可以獲得可以定位所有列表項的選擇器。如果你使用過 JQuery,則必須非常熟悉 $('div> p.title> a')。這將得到所有帖子,因為你只希望單獨獲取每個帖子的標題,所以必須遍歷每個帖子,這些操作是在 each() 函數的幫助下完成的。
要從每個標題中提取文本,必須在 Cheerio 的幫助下獲取 DOM元素( el 指代當前元素)。然后在每個元素上調用 text() 能夠為你提供文本。
現在,打開終端并運行 node crawler.js,然后你將看到大約存有標題的數組,它會很長。盡管這是一個非常簡單的用例,但它展示了 Cheerio 提供的 API 的簡單性質。
如果你的用例需要執行 Javascript 并加載外部源,那么以下幾個選項將很有幫助。
JSDOM:Node 的 DOM
JSDOM 是在 Node.js 中使用的文檔對象模型的純 Javascript 實現,如前所述,DOM 對 Node 不可用,但是 JSDOM 是最接近的。它或多或少地模仿了瀏覽器。
由于創建了 DOM,所以可以通過編程與要爬取的 Web 應用或網站進行交互,也可以模擬單擊按鈕。如果你熟悉 DOM 操作,那么使用 JSDOM 將會非常簡單。
const { JSDOM } = require('jsdom')
const { document } = new JSDOM(
'<h2 class="title">Hello world</h2>'
).window
const heading = document.querySelector('.title')
heading.textContent = 'Hello there!'
heading.classList.add('welcome')
heading.innerHTML
// <h2 class="title welcome">Hello there!</h2>
代碼中用 JSDOM 創建一個 DOM,然后你可以用和操縱瀏覽器 DOM 相同的方法和屬性來操縱該 DOM。
為了演示如何用 JSDOM 與網站進行交互,我們將獲得 Reddit r/programming 論壇的第一篇帖子并對其進行投票,然后驗證該帖子是否已被投票。
首先運行以下命令來安裝 jsdom 和 axios:npm install jsdom axios
然后創建名為 crawler.js的文件,并復制粘貼以下代碼:
const { JSDOM } = require("jsdom")
const axios = require('axios')
const upvoteFirstPost = async () => {
try {
const { data } = await axios.get("https://old.reddit.com/r/programming/");
const dom = new JSDOM(data, {
runScripts: "dangerously",
resources: "usable"
});
const { document } = dom.window;
const firstPost = document.querySelector("div > div.midcol > div.arrow");
firstPost.click();
const isUpvoted = firstPost.classList.contains("upmod");
const msg = isUpvoted
? "Post has been upvoted successfully!"
: "The post has not been upvoted!";
return msg;
} catch (error) {
throw error;
}
};
upvoteFirstPost().then(msg => console.log(msg));
upvoteFirstPost() 是一個異步函數,它將在 r/programming 中獲取第一個帖子,然后對其進行投票。axios 發送 HTTP GET 請求獲取指定 URL 的HTML。然后通過先前獲取的 HTML 來創建新的 DOM。JSDOM 構造函數把HTML 作為第一個參數,把 option 作為第二個參數,已添加的 2 個 option 項執行以下功能:
創建 DOM 后,用相同的 DOM 方法得到第一篇文章的 upvote 按鈕,然后單擊。要驗證是否確實單擊了它,可以檢查 classList 中是否有一個名為 upmod 的類。如果存在于 classList 中,則返回一條消息。
打開終端并運行 node crawler.js,然后會看到一個整潔的字符串,該字符串將表明帖子是否被贊過。盡管這個例子很簡單,但你可以在這個基礎上構建功能強大的東西,例如,一個圍繞特定用戶的帖子進行投票的機器人。
如果你不喜歡缺乏表達能力的 JSDOM ,并且實踐中要依賴于許多此類操作,或者需要重新創建許多不同的 DOM,那么下面將是更好的選擇。
Puppeteer:無頭瀏覽器
顧名思義,Puppeteer 允許你以編程方式操縱瀏覽器,就像操縱木偶一樣。它通過為開發人員提供高級 API 來默認控制無頭版本的 Chrome。
Puppeteer 比上述工具更有用,因為它可以使你像真正的人在與瀏覽器進行交互一樣對網絡進行爬取。這就具備了一些以前沒有的可能性:
它還可以在 Web 爬取之外的其他任務中發揮重要作用,例如 UI 測試、輔助性能優化等。
通常你會想要截取網站的屏幕截圖,也許是為了了解競爭對手的產品目錄,可以用 puppeteer 來做到。首先運行以下命令安裝 puppeteer,:npm install puppeteer
這將下載 Chromium 的 bundle 版本,根據操作系統的不同,該版本大約 180 MB 至 300 MB。如果你要禁用此功能。
讓我們嘗試在 Reddit 中獲取 r/programming 論壇的屏幕截圖和 PDF,創建一個名為 crawler.js的新文件,然后復制粘貼以下代碼:
const puppeteer = require('puppeteer')
async function getVisual() {
try {
const URL = 'https://www.reddit.com/r/programming/'
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto(URL)
await page.screenshot({ path: 'screenshot.png' })
await page.pdf({ path: 'page.pdf' })
await browser.close()
} catch (error) {
console.error(error)
}
}
getVisual()
getVisual() 是一個異步函數,它將獲 URL 變量中 url 對應的屏幕截圖和 pdf。首先,通過 puppeteer.launch() 創建瀏覽器實例,然后創建一個新頁面。可以將該頁面視為常規瀏覽器中的選項卡。然后通過以 URL 為參數調用 page.goto() ,將先前創建的頁面定向到指定的 URL。最終,瀏覽器實例與頁面一起被銷毀。
完成操作并完成頁面加載后,將分別使用 page.screenshot() 和 page.pdf() 獲取屏幕截圖和 pdf。你也可以偵聽 javascript load 事件,然后執行這些操作,在生產環境級別下強烈建議這樣做。
在終端上運行 node crawler.js ,幾秒鐘后,你會注意到已經創建了兩個文件,分別名為 screenshot.jpg 和 page.pdf。
Nightmare:Puppeteer 的替代者
Nightmare 是類似 Puppeteer 的高級瀏覽器自動化庫,該庫使用 Electron,但據說速度是其前身 PhantomJS 的兩倍。
如果你在某種程度上不喜歡 Puppeteer 或對 Chromium 捆綁包的大小感到沮喪,那么 nightmare 是一個理想的選擇。首先,運行以下命令安裝 nightmare 庫:npm install nightmare
然后,一旦下載了 nightmare,我們將用它通過 Google 搜索引擎找到 ScrapingBee 的網站。創建一個名為crawler.js的文件,然后將以下代碼復制粘貼到其中:
const Nightmare = require('nightmare')
const nightmare = Nightmare()
nightmare
.goto('https://www.google.com/')
.type("input[title='Search']", 'ScrapingBee')
.click("input[value='Google Search']")
.wait('#rso > div:nth-child(1) > div > div > div.r > a')
.evaluate(
() =>
document.querySelector(
'#rso > div:nth-child(1) > div > div > div.r > a'
).href
)
.end()
.then((link) => {
console.log('Scraping Bee Web Link': link)
})
.catch((error) => {
console.error('Search failed:', error)
})
首先創建一個 Nighmare 實例,然后通過調用 goto() 將該實例定向到 Google 搜索引擎,加載后,使用其選擇器獲取搜索框,然后使用搜索框的值(輸入標簽)更改為“ScrapingBee”。完成后,通過單擊 “Google搜索” 按鈕提交搜索表單。然后告訴 Nightmare 等到第一個鏈接加載完畢,一旦完成,它將使用 DOM 方法來獲取包含該鏈接的定位標記的 href 屬性的值。
最后,完成所有操作后,鏈接將打印到控制臺。
總結
紹 官網 github Vue.js 是一套構建用戶界面(UI)的漸進式 JavaScript 框架 庫和框架的區別 庫: ?> 庫,本質上是一些函數的集合。每次調用函數,實現一個特定的功能,接著把控制權交給使用者 jQuery:DOM 操作,即:封裝 DOM 操作,簡化 DOM 操作 框架: ?> 框架,是一套完整的解決方案,使用框架的時候,需要把你的代碼放到框架合適的地方,框架會在合適的時機調用你的代碼 框架規定了自己的編程方式,是一套完整的解決方案 使用框架的時候,由框架控制一切,我們只需要按照規則寫代碼 主要區別: !> 核心點:誰起到主導作用(控制反轉)框架的侵入性很高(從頭到尾) MVVM 和 MVC 的介紹: MVVM,一種更好的 UI 模式解決方案 MVVM===> M / V / VM M:model 數據模型 V:view 視圖 VM:ViewModel 視圖模型 MVC 模型-視圖-控制器 M: Model 數據模型(專門用來操作數據,數據的 CRUD) V:View 視圖(對于前端來說,就是頁面) C:Controller 控制器(是視圖和數據模型溝通的橋梁,用于處理業務邏輯) MVVM 和 MVC 的優勢對比 MVC 模式,將應用程序劃分為三大部分,實現了職責分離 在前端中經常要通過 JS 代碼 來進行一些邏輯操作,最終還要把這些邏輯操作的結果現在頁面中。也就是需要頻繁的操作 DOM MVVM 通過數據雙向綁定讓數據自動地雙向同步 V(修改數據) -> M M(修改數據) -> V 數據是核心 Vue 這種 MVVM 模式的框架,不推薦開發人員手動操作 DOM Vue 中的 MVVM 雖然沒有完全遵循 MVVM 模型,Vue 的設計無疑受到了它的啟發。因此在文檔中經常會使用 vm (ViewModel 的簡稱) 這個變量名表示 Vue 實例 !> 學習 Vue 要轉化思想 不要在想著怎么操作 DOM,而是想著如何操作數據!!! 起步 - Hello Vue 安裝:cnpm i -S vue {{ msg }}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Vue 實例 !> 注意 1:先在 data 中聲明數據,再使用數據 !> 注意 2:可以通過 vm.$data 或者 vm.msg 訪問到data中的所有屬性, var vm=new Vue({ data: { msg: "大家好,..." } }); vm.$data.msg===vm.msg; // true 1 2 3 4 5 6 7 1 2 3 4 5 6 7 數據綁定 最常用的方式:Mustache(插值語法),也就是 {{}} 語法 解釋:{{}}從數據對象data中獲取數據 說明:數據對象的屬性值發生了改變,插值處的內容都會更新 說明:{{}}中只能出現 JavaScript 表達式 而不能解析 js 語句 注意:Mustache 語法不能作用在 HTML 元素的屬性上
{{ 1 + 2 }}
{{ isOk ? 'yes': 'no' }}
1 2 3 4 5 6 1 2 3 4 5 6 數據雙向綁定 雙向數據綁定:將 DOM 與 Vue 實例的 data 數據綁定到一起,彼此之間相互影響 數據的改變會引起 DOM 的改變 DOM 的改變也會引起數據的變化 原理:Object.defineProperty中的get和set方法 getter和setter:訪問器 作用:指定讀取或設置對象屬性值的時候,執行的操作 深入響應式原理 /* defineProperty語法 介紹 */ var obj={}; Object.defineProperty(obj, "msg", { // 設置 obj.msg="1" 時set方法會被系統調用 參數分別是設置后和設置前的值 set: function(newVal, oldVal) {}, // 讀取 obj.msg 時get方法會被系統調用 get: function(newVal, oldVal) {} }); 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 Vue 雙向綁定的極簡實現 剖析 Vue 原理&實現雙向綁定 MVVM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 動態添加數據的注意點 注意:只有 data 中的數據才是響應式的,動態添加進來的數據默認為非響應式 可以通過以下方式實現動態添加數據的響應式 1 Vue.set(object, key, value) - 適用于添加單個屬性 2 Object.assign() - 適用于添加多個屬性 var vm=new Vue({ data: { stu: { name: "jack", age: 19 } } }); /* Vue.set */ Vue.set(vm.stu, "gender", "male"); /* Object.assign 將參數中的所有對象屬性和值 合并到第一個參數 并返回合并后的對象*/ vm.stu=Object.assign({}, vm.stu, { gender: "female", height: 180 }); 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 異步 DOM 更新 說明:Vue 異步執行 DOM 更新,監視所有數據改變,一次性更新 DOM 優勢:可以去除重復數據,對于避免不必要的計算和 避免重復 DOM 操作上,非常重要 如果需要那到更新后 dom 中的數據 則需要通過 Vue.nextTick(callback):在 DOM 更新后,執行某個操作(屬于 DOM 操作) 實例調用 vm.$nextTick(function () {}) methods: { fn() { this.msg='change' this.$nextTick(function () { console.log('$nextTick中打印:', this.$el.children[0].innerText); }) console.log('直接打印:', this.$el.children[0].innerText); } } 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 指令 解釋:指令 (Directives) 是帶有 v- 前綴的特殊屬性 作用:當表達式的值改變時,將其產生的連帶影響,響應式地作用于 DOM v-text 解釋:更新 DOM 對象的 textContent 1 2 1 2 v-html 解釋:更新 DOM 對象的 innerHTML 1 1 v-bind 作用:當表達式的值改變時,將其產生的連帶影響,響應式地作用于 DOM 語法:v-bind:title="msg" 簡寫::title="msg" 1 2 3 4 1 2 3 4 v-on 作用:綁定事件 語法:v-on:click=“say” or v-on:click=“say(‘參數’, $event)” 簡寫:@click=“say” 說明:綁定的事件定義在 methods 1 2 3 4 1 2 3 4 事件修飾符 .stop 阻止冒泡,調用 event.stopPropagation() .prevent 阻止默認行為,調用 event.preventDefault() .capture 添加事件偵聽器時使用事件捕獲模式 .self 只當事件在該元素本身(比如不是子元素)觸發時,才會觸發事件 .once 事件只觸發一次 v-model 作用:在表單元素上創建雙向數據綁定 說明:監聽用戶的輸入事件以更新數據 你輸入了: {{ message }}
1 2 1 2 v-for 作用:基于源數據多次渲染元素或模板塊 {{ item.text }}
{{item}} -- {{index}}
{{item}} -- {{key}}
{{item}}
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 key 屬性 推薦:使用 v-for 的時候提供 key 屬性,以獲得性能提升。 說明:使用 key,VUE 會基于 key 的變化重新排列元素順序,并且會移除 key 不存在的元素。
1 2 3 1 2 3 樣式處理 class 和 style 使用方式:v-bind:class=“expression” or :class=“expression” 表達式的類型:字符串、數組、對象(重點)===> 解析后===>解析后===>解析后 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 v-if 和 v-show v-if:根據表達式的值的真假條件,銷毀或重建元素 v-show:根據表達式之真假值,切換元素的 display CSS 屬性
這個元素展示出來了嗎???
這個元素,在HTML結構中嗎???
1 2 1 2 提升性能:v-pre vue 會跳過這個元素和它的子元素的編譯過程。可以用來顯示原始 Mustache 標簽。跳過大量沒有指令的節點會加快編譯。 {{ 這將不會被編譯 }} 1 1 提升性能:v-once 說明:vue 只渲染元素和組件一次。隨后的重新渲染,元素/組件及其所有的子節點將被視為靜態內容并跳過。這可以用于優化更新性能。 這不會被改變: {{msg}} 1 1 過濾器 filter 作用:文本數據格式化 過濾器可以用在兩個地方:{{}}和 v-bind 表達式 兩種過濾器:1.全局過濾器 2. 局部過濾器 全局過濾器 說明:通過全局方式創建的過濾器,在任何一個 vue 實例中都可以使用 注意:使用全局過濾器的時候,需要先創建全局過濾器,再創建 Vue 實例 顯示的內容由過濾器的返回值決定
{{ dateStr | date }}
{{ dateStr | date('YYYY-MM-DD hh:mm:ss') }}
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 局部過濾器 說明:局部過濾器是在某一個 vue 實例的內容創建的,只在當前實例中起作用 { data: {}, // 通過 filters 屬性創建局部過濾器 // 注意:此處為 filters filters: { filterName: function(value, format) {} } } 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 按鍵值修飾符 說明:在監聽鍵盤事件時,Vue 允許為 v-on 在監聽鍵盤事件時添加關鍵修飾符 其他:修飾鍵(.ctrl 等)、鼠標按鍵修飾符(.left 等) 鍵盤事件 - 鍵值修飾符 // 只有在 keyCode 是 13 時調用 vm.submit() @keyup.13="submit" // 使用全局按鍵別名 @keyup.enter="add" --- // 通過全局 config.keyCodes 對象自定義鍵值修飾符別名 Vue.config.keyCodes.f2=113 // 使用自定義鍵值修飾符 @keyup.enter.f2="add" 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 監視數據變化 - watch 概述:watch 是一個對象,鍵是需要觀察的表達式,值是對應回調函數 作用:當表達式的值發生變化后,會調用對應的回調函數完成響應的監視操作 vue$watch new Vue({ data: { a: 1, b: { age: 10 } }, watch: { a: function(val, oldVal) { // val 表示當前值 // oldVal 表示舊值 console.log("當前值為:" + val, "舊值為:" + oldVal); }, // 監聽對象屬性的變化 b: { handler: function(val, oldVal) { /* ... */ }, // deep : true表示是否監聽對象內部屬性值的變化 deep: true }, // 只監視user對象中age屬性的變化 "user.age": function(val, oldVal) {} } }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 計算屬性 說明:計算屬性是基于它們的依賴進行緩存的,只有在它的依賴發生改變時才會重新求值 注意:Mustache 語法({{}})中不要放入太多的邏輯,否則會讓模板過重、難以理解和維護 注意:computed 中的屬性不能與 data 中的屬性同名,否則會報錯 Vue computed 屬性原理 var vm=new Vue({ el: "#app", data: { firstname: "jack", lastname: "rose" }, computed: { fullname() { return this.firstname + "." + this.lastname; } } }); 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 實例生命周期 所有的 Vue 組件都是 Vue 實例,并且接受相同的選項對象即可 (一些根實例特有的選項除外)。 實例生命周期也叫做:組件生命周期 生命周期介紹 ?> 生命周期鉤子函數的定義:從組件被創建,到組件掛載到頁面上運行,再到頁面關閉組件被卸載,這三個階段總是伴隨著組件各種各樣的事件,這些事件,統稱為組件的生命周期函數! vue 生命周期鉤子函數 簡單說:一個組件從開始到最后消亡所經歷的各種狀態,就是一個組件的生命周期 注意:Vue 在執行過程中會自動調用生命周期鉤子函數,我們只需要提供這些鉤子函數即可 注意:鉤子函數的名稱都是 Vue 中規定好的! 鉤子函數 - beforeCreate() 說明:在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用 注意:此時,無法獲取 data 中的數據、methods 中的方法 鉤子函數 - created() 注意:這是一個常用的生命周期,可以調用 methods 中的方法、改變 data 中的數據 vue 實例生命周期 參考 1 vue 實例生命周期 參考 2 使用場景:發送請求獲取數據 鉤子函數 - beforeMounted() 說明:在掛載開始之前被調用 鉤子函數 - mounted() 說明:此時,vue 實例已經掛載到頁面中,可以獲取到 el 中的 DOM 元素,進行 DOM 操作 鉤子函數 - beforeUpdated() 說明:數據更新時調用,發生在虛擬 DOM 重新渲染和打補丁之前。你可以在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。 注意:此處獲取的數據是更新后的數據,但是獲取頁面中的 DOM 元素是更新之前的 鉤子函數 - updated() 說明:組件 DOM 已經更新,所以你現在可以執行依賴于 DOM 的操作。 鉤子函數 - beforeDestroy() 說明:實例銷毀之前調用。在這一步,實例仍然完全可用。 使用場景:實例銷毀之前,執行清理任務,比如:清除定時器等 鉤子函數 - destroyed() 說明:Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。 vue 單文件組件 vue-loader single-file components(單文件組件) 后綴名:.vue,該文件需要被預編譯后才能在瀏覽器中使用 注意:單文件組件依賴于兩個包 vue-loader / vue-template-compiler 安裝:cnpm i -D vue-loader vue-template-compiler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // webpack.config.js 配置: module: { rules: [ { test: /\.vue$/, loader: "vue-loader" } ]; } 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 使用單文件組件 /* main.js */ import Vue from "vue"; // 導入 App 組件 import App from "./App.vue"; const vm=new Vue({ el: "#app", // 通過 render 方法,渲染App組件 render: c=> c(App) }); 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 單文件組件使用步驟 1 安裝:cnpm i -D vue-loader vue-template-compiler 2 在 webpack.config.js 中配置 .vue 文件的 loader { test: /\.vue$/, use: 'vue-loader' } 3 創建 App.vue 單文件組件,注意:App 可以是任意名稱 4 在 main.js 入口文件中,導入 vue 和 App.vue 組件,通過 render 將組件與實例掛到一起 單文件組件+路由 vue - Vue.use Vue.use 和 路由 import Vue from "vue"; import App from "./App.vue"; // ------------- vue路由配置 開始 -------------- import Home from "./components/home/Home.vue"; import Login from "./components/login/Login.vue"; // 1 導入 路由模塊 import VueRouter from "vue-router"; // 2 ** 調用use方法使用插件 ** Vue.use(VueRouter); // 3 創建路由對象 const router=new VueRouter({ routes: [ { path: "/home", component: Home }, { path: "/login", component: Login } ] }); // ------------- vue路由配置 結束 -------------- const vm=new Vue({ el: "#app", render: c=> c(App), // 4 掛載到 vue 實例中 router }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Mint-UI 基于 Vue.js 的移動端組件庫 Mint-UI 安裝:cnpm i -S mint-ui // 1 導入 mint-ui模塊 import MintUI from "mint-ui"; // 2 導入 樣式 import "mint-ui/lib/style.css"; // 3 注冊插件 Vue.use(MintUI); 1 2 3 4 5 6 1 2 3 4 5 6 MUI MUI MUI 也是移動端的 UI 庫 使用:從 github 下載包,找到 dist 文件夾,只需要導入樣式即可 // 只需要導入 MUI的樣式 即可,根據MUI的例子,直接使用HTML結果即可 // 導入樣式 import "./lib/mui/css/mui.min.css"; 1 2 3 1 2 3 ElementUI 這是 PC 端的 UI 組件庫 安裝:cnpm i -S element-ui 餓了么 - ElementUI { "presets": [ ["es2015", { "modules": false }], "stage-0" ], "plugins": [ ["component", [ { "libraryName": "mint-ui", "style": true }, { "libraryName": "element-ui", "styleLibraryName": "theme-default" } ]] ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 axios 以 Promise 為基礎的 HTTP 客戶端,適用于:瀏覽器和 node.js 封裝 ajax,用來發送請求,異步獲取數據 安裝:cnpm i -S axios github axios 文檔 // 在瀏覽器中使用,直接引入js文件使用下面的GET/POST請求方式即可 // 1 引入 axios.js // 2 直接調用axios提供的API發送請求 created: function () { axios.get(url) .then(function(resp) {}) } // 配合 webpack 使用方式如下: import Vue from 'vue' import axios from 'axios' // 將 axios 添加到 Vue.prototype 中 Vue.prototype.$axios=axios // 在組件中使用: methods: { getData() { this.$axios.get('url') .then(res=> {}) .catch(err=> {}) } } // API使用方式: axios.get(url[, config]) axios.post(url[, data[, config]]) axios(url[, config]) axios(config) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 axios - Get 請求 const url="http://vue.studyit.io/api/getnewslist"; // url中帶有query參數 axios .get("/user?id=89") .then(function(response) { console.log(response); }) .catch(function(error) { console.log(error); }); // url和參數分離,使用對象 axios.get("/user", { params: { id: 12345 } }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 axios - Post 請求 不同環境中處理 POST 請求 ?> 默認情況下,axios 會將 JS 對象序列化為 JSON 對象。為了使用 application/x-www-form-urlencoded 格式發送請求,我們可以這樣: // 使用 qs 包,處理將對象序列化為字符串 // npm i -S qs // var qs=require('qs') import qs from 'qs' qs.stringify({ 'bar': 123 })===> "bar=123" axios.post('/foo', qs.stringify({ 'bar': 123 })) // 或者: axios.post('/foo', 'bar=123&age=19') 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 const url="http://vue.studyit.io/api/postcomment/17"; axios.post(url, "content=點個贊不過份"); axios .post( "/user", qs.stringify({ firstName: "Fred", lastName: "Flintstone" }) ) .then(function(response) { console.log(response); }) .catch(function(error) { console.log(error); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 axios 全局配置 // 設置請求公共路徑: axios.defaults.baseURL="http://vue.studyit.io"; 1 2 1 2 axios 攔截器 攔截器會攔截發送的每一個請求,請求發送之前執行 request 中的函數,請求發送完成之后執行 response 中的函數 // 請求攔截器 axios.interceptors.request.use( function(config) { // 所有請求之前都要執行的操作 return config; }, function(error) { // 錯誤處理 return Promise.reject(error); } ); // 響應攔截器 axios.interceptors.response.use( function(response) { // 所有請求完成后都要執行的操作 return response; }, function(error) { // 錯誤處理 return Promise.reject(error); } ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 vue 自定義指令 作用:進行 DOM 操作 使用場景:對純 DOM 元素進行底層操作,比如:文本框獲得焦點 vue 自定義指令用法實例 兩種指令:1. 全局指令 2. 局部指令 全局自定義指令 // 第一個參數:指令名稱 // 第二個參數:配置對象,指定指令的鉤子函數 Vue.directive('directiveName', { // bind中只能對元素自身進行DOM操作,而無法對父級元素操作 // 只調用一次 指令第一次綁定到元素時調用。在這里可以進行一次性的初始化設置。 bind( el,binding, vnode ) { // 參數詳解 // el:指令所綁定的元素,可以用來直接操作 DOM 。 // binding:一個對象,包含以下屬性: // name:指令名,不包括 v- 前綴。 // value:指令的綁定值,等號后面的值 。 // oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。 // expression:字符串形式的指令表達式 等號后面的字符串 形式 // arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數為 "foo"。 // modifiers:指令修飾符。例如:v-directive.foo.bar中,修飾符對象為 { foo: true, bar: true }。 // vnode:Vue 編譯生成的虛擬節點。。 // oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。 }, // inserted這個鉤子函數調用的時候,當前元素已經插入頁面中了,也就是說可以獲取到父級節點了 inserted ( el,binding, vnode ) {}, // DOM重新渲染前 update(el,binding, vnode,oldVnode) {}, // DOM重新渲染后 componentUpdated ( el,binding, vnode,oldVnode ) {}, // 只調用一次,指令與元素解綁時調用 unbind ( el ) { // 指令所在的元素在頁面中消失,觸發 } }) // 簡寫 如果你想在 bind 和 update 時觸發相同行為,而不關心其它的鉤子: Vue.directive('自定義指令名', function( el, binding ) {}) // 例: Vue.directive('color', function(el, binding) { el.style.color=binging.value }) // 使用 注意直接些會被i成data中的數據“red” 需要字符串則嵌套引號"'red'" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 局部自定義指令 vue 剖析 Vue 原理&實現雙向綁定 MVVM var vm=new Vue({ el: "#app", directives: { directiveName: {} } }); 1 2 3 4 5 6 1 2 3 4 5 6 組件 組件系統是 Vue 的另一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可復用的組件構建大型應用。仔細想想,幾乎任意類型的應用界面都可以抽象為一個組件樹 #### 全局組件 說明:全局組件在所有的 vue 實例中都可以使用 注意:先注冊組件,再初始化根實例 // 1 注冊全局組件 Vue.component('my-component', { // template 只能有一個根元素 template: '
A custom component!
', // 組件中的 `data` 必須是函數 并且函數的返回值必須是對象 data() { return { msg: '注意:組件的data必須是一個函數!!!' } } }) // 2 使用:以自定義元素的方式
//=====> 渲染結果
A custom component!
// 3 template屬性的值可以是: - 1 模板字符串 - 2 模板id template: '#tpl' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 extend:使用基礎 Vue 構造器,創建一個“子類”。參數是一個包含組件選項的對象。 // 注冊組件,傳入一個擴展過的構造器 Vue.component( "my-component", Vue.extend({ /* ... */ }) ); // 注冊組件,傳入一個選項對象 (自動調用 Vue.extend) Vue.component("my-component", { /* ... */ }); var Home=Vue.extend({ template: "", data() {} }); Vue.component("home", Home); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 局部組件 說明:局部組件,是在某一個具體的 vue 實例中定義的,只能在這個 vue 實例中使用 var Child={ template: "
A custom component!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 組件通訊 父組件到子組件(父子組件傳值) 方式:通過子組件props屬性來傳遞數據 props是一個數組 注意:屬性的值必須在組件中通過props屬性顯示指定,否則,不會生效 說明:傳遞過來的props屬性的用法與data屬性的用法相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 子組件到父組件 方式:父組件給子組件傳遞一個函數,由子組件調用這個函數 說明:借助 vue 中的自定義事件(v-on:cunstomFn=“fn”) 步驟: 1、在父組件中定義方法 parentFn 2、在子組件 組件引入標簽 中綁定自定義事件 v-on:自定義事件名=“父組件中的方法”==> @pfn=“parentFn” 3、子組件中通過emit()觸發自定義事件事件this. emit()觸發自定義事件事件 this.emit()觸發自定義事件事件this.emit(pfn,參數列表。。。) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 非父子組件通訊 ?> 在簡單的場景下,可以使用一個空的 Vue 實例作為事件總線 $on():綁定自定義事件 var bus=new Vue(); // 在組件 B 綁定自定義事件 bus.$on("id-selected", function(id) { // ... }); // 觸發組件 A 中的事件 bus.$emit("id-selected", 1); 1 2 3 4 5 6 7 1 2 3 4 5 6 7 示例:組件 A —> 組件 B 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 內容分發 通過 標簽指定內容展示區域 我是額外的內容
1 2 3 4 5 6 7 1 2 3 4 5 6 7 // js代碼 new vue({ el: "#app", components: { hello: { template: `
我是子組件中的內容
` } } }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 獲取組件(或元素) - refs 說明:vm.$refs 一個對象,持有已注冊過 ref 的所有子組件(或 HTML 元素) 使用:在 HTML 元素 中,添加 ref 屬性,然后在 JS 中通過 vm.$refs.屬性來獲取 注意:如果獲取的是一個子組件,那么通過 ref 就能獲取到子組件中的 data 和 methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 SPA -單頁應用程序 SPA: Single Page Application ?> 單頁 Web 應用(single page application,SPA),就是只有一個 Web 頁面的應用, 是加載單個 HTML 頁面,并在用戶與應用程序交互時動態更新該頁面的 Web 應用程序。 單頁面應用程序: 只有第一次會加載頁面, 以后的每次請求, 僅僅是獲取必要的數據.然后, 由頁面中 js 解析獲取的數據, 展示在頁面中 傳統多頁面應用程序: 對于傳統的多頁面應用程序來說, 每次請求服務器返回的都是一個完整的頁面 優勢 1 減少了請求體積,加快頁面響應速度,降低了對服務器的壓力 2 更好的用戶體驗,讓用戶在 web app 感受 native app 的流暢 實現思路和技術點 1 ajax 2 錨點的使用(window.location.hash #) 3 hashchange 事件 window.addEventListener(“hashchange”,function () {}) 4 監聽錨點值變化的事件,根據不同的錨點值,請求相應的數據 5 原本用作頁面內部進行跳轉,定位并展示相應的內容 路由 路由即:瀏覽器中的哈希值(# hash)與展示視圖內容(template)之間的對應規則 vue 中的路由是:hash 和 component 的對應關系 在 Web app 中,通過一個頁面來展示和管理整個應用的功能。 SPA 往往是功能復雜的應用,為了有效管理所有視圖內容,前端路由 應運而生! 簡單來說,路由就是一套映射規則(一對一的對應規則),由開發人員制定規則。 當 URL 中的哈希值(# hash)發生改變后,路由會根據制定好的規則,展示對應的視圖內容 基本使用 安裝:cnpm i -S vue-router
首頁 登錄
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 重定向 // 將path 重定向到 redirect { path: '/', redirect: '/home' } 1 2 1 2 路由其他配置 路由導航高亮 說明:當前匹配的導航鏈接,會自動添加 router-link-exact-active router-link-active 類 配置:linkActiveClass 匹配路由模式 配置:mode new Router({ routers: [], mode: "hash", //默認hash | history 可以達到隱藏地址欄hash值 | abstract,如果發現沒有瀏覽器的 API 則強制進入 linkActiveClass: "now" //當前匹配的導航鏈接將被自動添加now類 }); 1 2 3 4 5 1 2 3 4 5 路由參數 說明:我們經常需要把某種模式匹配到的所有路由,全都映射到同一個組件,此時,可以通過路由參數來處理 語法:/user/:id 使用:當匹配到一個路由時,參數值會被設置到 this.$route.params 其他:可以通過 $route.query 獲取到 URL 中的查詢參數 等 如果你需要在模版中使用路由參數 可以這樣 {{$router.params.id}} 用戶 Rose 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 嵌套路由 - 子路由 路由是可以嵌套的,即:路由中又包含子路由 規則:父組件中包含 router-view,在路由規則中使用 children 配置 // 父組件: const User=Vue.component("user", { template: `
User Center
個人資料 崗位
` }); // 子組件[簡寫] const UserProfile={ template: "
個人資料:張三
" }; const UserPosts={ template: "
崗位:FE
" }; // 路由 var router=new Router({ routers: [ { path: "/user", component: User, // 子路由配置: children: [ { // 當 /user/profile 匹配成功, // UserProfile 會被渲染在 User 的 中 path: "profile", component: UserProfile }, { // 當 /user/posts 匹配成功 // UserPosts 會被渲染在 User 的 中 path: "posts", component: UserPosts } ] } ] }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 前端模塊化 ?> 為什么需要模塊化 最開始的 js 就是為了實現客戶端驗證以及一些簡單的效果 后來,js 得到重視,應用越來越廣泛,前端開發的復雜度越來越高 舊版本的 js 中沒有提供與模塊(module)相關的內容 模塊的概念 在 js 中,一個模塊就是實現特定功能的文件(js 文件) 遵循模塊的機制,想要什么功能就加載什么模塊 模塊化開發需要遵循規范 模塊化解決的問題 命名沖突 文件依賴(加載文件) 模塊的復用 統一規范和開發方式 JS 實現模塊化的規范 AMD 瀏覽器端 requirejs CommonJS nodejs 加載模塊:require() 導出模塊:module.exports={} / exports={} ES6 中的 import / export CMD 瀏覽器端 玉伯(阿里前端大神) -> seajs UMD 通用模塊化規范,可以兼容 AMD、CommonJS、瀏覽器中沒有模塊化規范 等這些語法 AMD 的使用 Asynchronous Module Definition:異步模塊定義,瀏覽器端模塊開發的規范 代表:require.js 特點:模塊被異步加載,模塊加載不影響后面語句的運行 1、定義模塊 // 語法:define(name, dependencies?, factory); // name表示:當前模塊的名稱,是一個字符串 可有可無 // dependencies表示:當前模塊的依賴項,是一個數組無論依賴一項還是多項 無則不寫 // factory表示:當前模塊要完成的一些功能,是一個函數 // 定義對象模塊 define({}); // 定義方法模塊 define(function() { return {}; }); // 定義帶有依賴項的模塊 define(["js/a"], function() {}); 1 2 3 4 5 6 7 8 9 10 11 12 13 1 2 3 4 5 6 7 8 9 10 11 12 13 2、加載模塊 // - 注意:require 的第一個參數必須是數組 // 參數必須是數組 表示模塊路徑 以當前文件為基準,通過回調函數中的參數獲取加載模塊中的變量 參數與模塊按照順序一一對應 require(["a", "js/b"], function(a, b) { // 使用模塊a 和 模塊b 中的代碼 }); 1 2 3 4 5 1 2 3 4 5 3、路徑查找配置 requirejs 默認使用 baseUrl+paths 的路徑解析方式 可以使用以下方式避開此設置: 以.js結尾 以 / 開始 包含協議:https:// 或 http:// // 配置示例 // 注意配置應當在使用之前 require.config({ baseUrl: "./js" // 配置基礎路徑為:當前目錄下的js目錄 }); require(["a"]); // 查找 基礎路徑下的 ./js/a.js // 簡化加載模塊路徑 require.config({ baseUrl: "./js", // 配置一次即可,直接通過路徑名稱(template || jquery)加載模塊 paths: { template: "assets/artTemplate/template-native", jquery: "assets/jquery/jquery.min" } }); // 加載jquery template模塊 require(["jquery", "template"]); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 4、非模塊化和依賴項支持 添加模塊的依賴模塊,保證加載順序(deps) 將非模塊化模塊,轉化為模塊化(exports) // 示例 require.config({ baseUrl: "./js", paths: { // 配置路徑 noModule: "assets/demo/noModule" }, // 配置不符合規范的模塊項 shim: { // 模塊名稱 noModule: { deps: [], // 依賴項 exports: "sayHi" // 導出模塊中存在的函數或變量 } } }); // 注意點 如果定義模塊的時候,指定了模塊名稱,需要使用該名稱來引用模塊 // 定義 這個模塊名稱與paths中的名稱相同 define("moduleA", function() {}); // 導入 require.config({ paths: { // 此處的模塊名:moduleA moduleA: "assets/demo/moduleA" } }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 5、路徑加載規則 路徑配置的優先級: 通過 config 配置規則查找 通過 data-main 指定的路徑查找 以引入 requirejs 的頁面所在路徑為準查找 1 2 3 4 5 6 1 2 3 4 5 6 Webpack webpack 文檔 bundle [?b?ndl] 捆綁,收集,歸攏,把…塞入 webpack 將帶有依賴項的各個模塊打包處理后,變成了獨立的瀏覽器能夠識別的文件 webpack 合并以及解析帶有依賴項的模塊 概述 webpack 是一個現代 JavaScript 應用程序的模塊打包器(特點 module、 bundler) webpack 是一個模塊化方案(預編譯) webpack 獲取具有依賴關系的模塊,并生成表示這些模塊的靜態資源 四個核心概念:入口(entry)、輸出(output)、加載器loader、插件(plugins) 對比 模塊化方案: webpack 和 requirejs(通過編寫代碼的方式將前端的功能,劃分成獨立的模塊) browserify 是與 webpack 相似的模塊化打包工具 webpack 預編譯 (在開發階段通過 webpack 進行模塊化處理, 最終項目上線, 就不在依賴于 webpack) requirejs 線上的編譯( 代碼運行是需要依賴于 requirejs 的 ) webpack 起源 webpack 解決了現存模塊打包器的兩個痛點: 1 Code Spliting - 代碼分離 按需加載 2 靜態資源的模塊化處理方案 webpack 與模塊 前端模塊系統的演進 在 webpack 看來:所有的靜態資源都是模塊 webpack 模塊能夠識別以下等形式的模塊之間的依賴: JS 的模塊化規范: ES2015 import export CommonJS require() module.exports AMD define 和 require 非 JS 等靜態資源: css/sass/less 文件中的 @import 圖片連接,比如:樣式 url(...) 或 HTML
字體 等 入門 Webpack,看這篇就夠了 安裝 webpack 全局安裝:cnpm i -g webpack 目的:在任何目錄中通過 CLI 使用 webpack 這個命令 項目安裝:cnpm i -D webpack 目的:執行當前項目的構建 webpack 的基本使用 安裝:npm i -D webpack webpack 的兩種使用方式:1 命令行 2 配置文件(webpack.config.js) 命令行方式演示 - 案例:隔行變色 1 使用npm init -y 初始package.json,使用 npm 來管理項目中的包 2 新建 index.html 和 index.js,實現隔行變色功能 3 運行webpack src/js/index.js dist/bundle.js進行打包構建,語法是:webpack 入口文件 輸出文件 4 注意:需要在頁面中引入 輸出文件 的路徑(此步驟可通過配置 webpack 去掉) /* src/js/index.js */ // 1 導入 jQuery import $ from "jquery"; // 2 獲取頁面中的li元素 const $lis=$("#ulList").find("li"); // 3 隔行變色 // jQuery中的 filter() 方法用來過濾jquery對象 $lis.filter(":odd").css("background-color", "#def"); $lis.filter(":even").css("background-color", "skyblue"); //命令行運行 `webpack src/js/index.js dist/bundle.js 目錄生成在命令行運行目錄 /* 運行流程: 1、webpack 根據入口找到入口文件 2、分析js中的模塊化語法 3、將所有關聯文件 打包合并輸出到出口 */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 webpack-dev-server 配置 一、package.json 配置方式 安裝:cnpm i -D webpack-dev-server 作用:配合 webpack,創建開發環境(啟動服務器、監視文件變化、自動編譯、刷新瀏覽器等),提高開發效率 注意:無法直接在終端中執行 webpack-dev-server,需要通過 package.json 的 scripts 實現 使用方式:cnpm run dev // 參數解釋 注意參數是無序的 有值的參數空格隔開 // --open 自動打開瀏覽器 // --contentBase ./ 指定瀏覽器 默認打開的頁面路徑中的 index.html 文件 // --open 自動打開瀏覽器 // --port 8080 端口號 // --hot 熱更新,只加載修改的文件(按需加載修改的內容),而非全部加載 "scripts": { "dev": "webpack-dev-server --open --contentBase ./ --port 8080 --hot" } 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 二、webpack.config.js 配置方式(推薦) var path=require('path') module.exports={ // 入口文件 entry: path.join(__dirname, 'src/js/index.js'), // 輸出文件 output: { path: path.join(__dirname, 'dist'), // 輸出文件的路徑 filename: 'bundle.js' // 輸出文件的名稱 } } const webpack=require('webpack') devServer: { // 服務器的根目錄 Tell the server where to serve content from // https://webpack.js.org/configuration/dev-server/#devserver-contentbase contentBase: path.join(__dirname, './'), // 自動打開瀏覽器 open: true, // 端口號 port: 8888, // --------------- 1 熱更新 ----------------- hot: true }, plugins: [ // ---------------- 2 啟用熱更新插件 ---------------- new webpack.HotModuleReplacementPlugin() ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 html-webpack-plugin 插件 安裝:cnpm i -D html-webpack-plugin 作用:根據模板,自動生成 html 頁面 優勢:頁面存儲在內存中,自動引入bundle.js、css等文件 /* webpack.config.js */ const htmlWebpackPlugin=require("html-webpack-plugin"); plugins: [ new htmlWebpackPlugin({ // 模板頁面路徑 template: path.join(__dirname, "./index.html"), // 在內存中生成頁面路徑,默認值為:index.html filename: "index.html" }) ]; 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 Loaders(加載器) webpack - Loaders webpack - 管理資源示例 webpack 只能處理 JavaScript 資源 webpack 通過 loaders 處理非 JavaScript 靜態資源 1、 CSS 打包 安裝:cnpm i -D style-loader css-loader 注意:use 中模塊的順序不能顛倒,加載順序:從右向左加載 /* 在index.js 導入 css 文件*/ import "./css/app.css"; /* webpack.config.js 配置各種資源文件的loader加載器*/ module: { // 配置匹配規則 rules: [ // test 用來配置匹配文件規則(正則) // use 是一個數組,按照從后往前的順序執行加載 { test: /\.css$/, use: ["style-loader", "css-loader"] } ]; } 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 2、 使用 webpack 打包 sass 文件 安裝:cnpm i -D sass-loader node-sass 注意:sass-loader 依賴于 node-sass 模塊 /* webpack.config.js */ // 參考:https://webpack.js.org/loaders/sass-loader/#examples // "style-loader" :creates style nodes from JS strings 創建style標簽 // "css-loader" :translates CSS into CommonJS 將css轉化為CommonJS代碼 // "sass-loader" :compiles Sass to CSS 將Sass編譯為css module: { rules: [ { test: /\.(scss|sass)$/, use: ["style-loader", "css-loader", "sass-loader"] } ]; } 1 2 3 4 5 6 7 8 9 10 11 12 13 1 2 3 4 5 6 7 8 9 10 11 12 13 3、 圖片和字體打包 安裝:cnpm i -D url-loader file-loader file-loader:加載并重命名文件(圖片、字體 等) url-loader:將圖片或字體轉化為 base64 編碼格式的字符串,嵌入到樣式文件中 /* webpack.config.js */ module: { rules: [ // 打包 圖片文件 { test: /\.(jpg|png|gif|jpeg)$/, use: "url-loader" }, // 打包 字體文件 { test: /\.(woff|woff2|eot|ttf|otf)$/, use: "file-loader" } ]; } 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 圖片打包細節 limit 參數的作用:(單位為:字節(byte)) 當圖片文件大小(字節)小于指定的 limit 時,圖片被轉化為base64編碼格式 當圖片文件大小(字節)大于等于指定的 limit 時,圖片被重命名以 url 路徑形式加載(此時,需要 file-loader 來加載圖片) 圖片文件重命名,保證相同文件不會被加載多次。例如:一張圖片(a.jpg)拷貝一個副本(b.jpg),同時引入這兩張圖片,重命名后只會加載一次,因為這兩張圖片就是同一張 文件重命名以后,會通過 MD5 加密的方式,來計算這個文件的名稱 /* webpack.config.js */ module: { rules: [ // {test: /\.(jpg|png|gif|jpeg)$/, use: 'url-loader?limit=100'}, { test: /\.(jpg|png|gif|jpeg)$/, use: [ { loader: "url-loader", options: { limit: 8192 } } ] } ]; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 字體文件打包說明 處理方式與圖片相同,可以使用:file-loader或url-loader babel babel babel 全家桶 安裝:cnpm i -D babel-core babel-loader 安裝:cnpm i -D babel-preset-env 基本使用(兩步) 第一步: /* webpack.config.js */ module: { rules: [ // exclude 排除,不需要編譯的目錄,提高編譯速度 { test: /\.js$/, use: "babel-loader", exclude: /node_modules/ } ]; } 1 2 3 4 5 6 7 1 2 3 4 5 6 7 第二步:在項目根目錄中新建.babelrc配置文件 /* 創建 .babelrc 文件*/ // 將來babel-loader運行的時候,會檢查這個配置文件,并讀取相關的語法和插件配置 { "presets": ["env"] } 1 2 3 4 5 1 2 3 4 5 babel 的說明 babel 的作用: 1 語法轉換:將新的 ES 語法轉化為瀏覽器能識別的語法(babel-preset-*) 2 polyfill 瀏覽器兼容:讓低版本瀏覽器兼容最新版 ES 的 API babel-preset-* Babel 通過語法轉換器,能夠支持最新版本的 JavaScript 語法 babel-preset-* 用來指定我們書寫的是什么版本的 JS 代碼 作用:將新的 ES 語法轉化為瀏覽器能識別的 ES5 代碼 ES6 語法提案的批準流程 ES2015 也就是 ES6, 下一個版本是 ES7, 從 ES6 到 ES7 之間經歷了 5 個階段 babel-preset-es2015 轉換 es6 的語法 babel-preset-stage-0 轉換比 es6 更新的語法 Stage 0 - Strawman(展示階段) Stage 1 - Proposal(征求意見階段) Stage 2 - Draft(草案階段) Stage 3 - Candidate(候選人階段) Stage 4 - Finished(定案階段) 1 2 3 4 5 總結 babel-core babel 核心包 babel-loader 用來解析 js 文件 babel-preset-* 新 ES 語法的解析和轉換 transform-runtime / babel-polyfill 兼容舊瀏覽器,到達支持新 API 目的 // 判斷瀏覽器是否兼容 padStart 這個 API if (!String.prototype.padStart) { // 如果不兼容, 就自己模擬 padStart的功能實現一份 String.prototype.padStart=function padStart(targetLength, padString) {}; } 1 2 3 4 5 1 2 3 4 5 Webpack 發布項目 webpack 打包的各種坑 webpack 命令能夠生成dist目錄到磁盤中,最終,把打包后的代碼,部署服務器中去 webpack-dev-server 僅是在內存中生成的文件,并沒有寫到磁盤中,所以,只能在開發期間使用 創建項目發布配置文件 開發期間配置文件:webpack.config.js 項目發布配置文件:webpack.prod.js (文件名稱非固定 production 生產環境) 命令:webpack --config webpack.prod.js 指定配置文件名稱運行 webpack 參數:–display-error-details 用于顯示 webpack 打包的錯誤信息 /* package.json */ "scripts": { "build": "webpack --config webpack.prod.js" } 1 2 3 4 5 1 2 3 4 5 在項目根目錄中創建 webpack.prod.js 文件 在 package.json 中, 配置一個 scripts 在 終端中 通過 npm run build 對項目進行打包 打包處理過程 刪除掉 devServer 相關的配置項 將圖片和字體文件輸出到指定的文件夾中 自動刪除 dist 目錄 分離第三方包(將使用的 vue 等第三方包抽離到 vender.js 中) 壓縮混淆 JS 以及 指定生成環境 抽取和壓縮 CSS 文件 壓縮 HTML 頁面 配合 vue 的異步組件,實現按需加載功能 處理圖片路徑 注意:如果 limit 小于比圖片大,那么圖片將被轉化為 base64 編碼格式 name 參數介紹 /* webpack.prod.js */ // 處理URL路徑的loader { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: { loader: 'url-loader', options: { limit: 8192, name: 'images/[hash:7].[ext]' // 作用:將圖片輸出到images文件夾中,文件名采用7位的哈希值(MD5),并且保持原來的圖片文件擴展名 // name:指定文件輸出路徑和輸出文件命令規則 // [hash:7]:表示使用7位哈希值代表文件名稱 // [ext]:表示保持文件原有后綴名 // name: 'imgs/img-[hash:7].[ext]' } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 自動刪除 dist 目錄 安裝:cnpm i -D clean-webpack-plugin 作用: 每次打包之前, 刪除上一次打包的 dist 目錄 /* webpack.prod.js */ const cleanWebpackPlugin=require("clean-webpack-plugin"); plugins: [ // 創建一個刪除文件夾的插件,刪除dist目錄 new cleanWebpackPlugin(["./dist"]) ]; 1 2 3 4 5 6 7 1 2 3 4 5 6 7 分離第三方包 目的:將公共的第三方包,抽離為一個單獨的包文件,這樣防止重復打包! 例如:main.js、router、vuex中都引入了 vue,不分離的話,vue 會被打包 3 次 抽離后, vue 文件只會被打包一次, 用到的地方僅僅是引用 /* webpack.prod.js */ // 1 入口 -- 打包文件的入口 entry: { // 項目代碼入口 app: path.join(__dirname, './src/js/main.js'), // 第三方包入口 vendor: ['vue', 'vue-router', 'axios'] }, output: { // 2 修改輸出文件路徑和命名規則 filename: 'js/[name].[chunkhash].js', }, plugins: [ // 3 抽離第三方包 new webpack.optimize.CommonsChunkPlugin({ // 將 entry 中指定的 ['vue', 'vue-router', 'axios'] 打包到名為 vendor 的js文件中 // 第三方包入口名稱,對應 entry 中的 vendor 屬性 name: 'vendor', }), ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 壓縮混淆 JS 注意:uglifyjs 無法壓縮 ES6 的代碼 plugins: [ // 優化代碼 // https://github.com/webpack-contrib/uglifyjs-webpack-plugin/tree/v0.4.6 new webpack.optimize.UglifyJsPlugin({ // 壓縮 compress: { // 移除警告 warnings: false } }), // 指定環境為生產環境:vue會根據這一項啟用壓縮后的vue文件 new webpack.DefinePlugin({ "process.env": { NODE_ENV: JSON.stringify("production") } }) ]; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 抽取和壓縮 CSS 文件 安裝:抽離 cnpm i -D extract-text-webpack-plugin 安裝:壓縮 cnpm i -D optimize-css-assets-webpack-plugin webpack 抽離 CSS 文檔 壓縮抽離后的 CSS 壓縮和抽離 CSS 報錯的說明: Error processing file: css/style.css postcss-svgo: Error in parsing SVG: Unquoted attribute value 1 2 1 2 原因:壓縮和抽離 CSS 的插件中只允許 SVG 使用雙引號 /* webpack.prod.js */ // 分離 css 到獨立的文件中 const ExtractTextPlugin=require("extract-text-webpack-plugin"); // 壓縮 css 資源文件 const OptimizeCssAssetsPlugin=require('optimize-css-assets-webpack-plugin') // bug描述: 生成后面的css文件中圖片路徑錯誤,打開頁面找不到圖片 // 解決:google搜索 webpack css loader 樣式圖片路徑 output: { // https://doc.webpack-china.org/configuration/output/#output-publicpath // 設置公共路徑 publicPath: '/', }, module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" }) }, { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: ['css-loader', 'sass-loader'] }) }, ] }, plugins: [ // 通過插件抽離 css (參數) new ExtractTextPlugin("css/style.css"), // 抽離css 的輔助壓縮插件 new OptimizeCssAssetsPlugin() ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 壓縮 HTML 頁面 詳細的配置可以參考html-minifier new htmlWebpackPlugin({ // 模板頁面 template: path.join(__dirname, './index.html'), // 壓縮HTML minify: { // 移除空白 collapseWhitespace: true, // 移除注釋 removeComments: true, // 移除屬性中的雙引號 removeAttributeQuotes: true } }), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vue 配合 webpack 實現路由按需加載 Vue 路由懶加載 Vue 異步組件 Vue 組件懶加載淺析 步驟 1 修改組件的引用方式 // 方式一: require.ensure() const NewsList=r=> require.ensure( [], ()=> r(require("../components/news/newslist.vue")), "news" ); // 方式二: import() -- 推薦 // 注意:/* webpackChunkName: "newsinfo" */ 是一個特殊的語法,表示生成js文件的名稱 const NewsInfo=()=> import(/* webpackChunkName: "newsinfo" */ "../components/news/newsinfo.vue"); 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 2 修改 webpack 配置文件的 output output: { // ------添加 chunkFilename, 指定輸出js文件的名稱------ chunkFilename: 'js/[name].[chunkhash].js', }
*請認真填寫需求信息,我們會在24小時內與您取得聯系。