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
vue.js 正如官網所說的,是一套構建用戶界面的漸進式框架。與其它重量級框架不同的是,vue 被設計為可以自底向上逐層應用。vue 的核心庫只關注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另外一方面,當與現代化工具鏈以及各種支持類庫結合使用時,vue 也完全能夠為復雜的單頁應用提供驅動。
vue.js 有聲明式,響應式的數據綁定,組件化開發,并且還使用虛擬 DOM 等技術,統一編程規范和模塊等,將項目功能模塊化更方便組織和構建復雜應用,便于項目的擴展和維護。 vue 框架維護及時,且 Vue 3 將在 2022 年 2 月 7 日 成為新的默認版本。
會執行的鉤子函數以及它們的順序分別為:
beforeCreat、created、beforeMount、mounted、activated
key 的作用主要是為了高效地更新虛擬 DOM,其原理是 vue 中在 patch 過程中,通過 key 可以精準判斷兩個節點是否是同一個,從而避免頻繁更新不同元素,使得整個 patch 過程更加高效,減少 DOM 操作量,提高性能。
另外,若不設置 key 還可能在列表更新時,引發一些隱蔽的 bug 。vue 在使用相同標簽名元素的過濾或切換時,也會使用到 key 屬性,其目的也是為了讓 vue 可以區分它們,否則 vue 只會替換其內部屬性而不會觸發過濾效果。
v-for 的優先級更高。
如果 v-if 和 v-for 同時出現,每次渲染都會先執行循環,再判斷條件,無論如何循環都不可避免,浪費了性能。
情景一:每次遍歷時,都需要執行 v-if 解析一次,浪費性能。
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
要避免出現這種情況,則在外層嵌套 template ,在這一層進行 v-if 判斷,然后在內部進行 v-for 循環。可以改為:
<ul>
<template v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</template>
</ul>
情景二:v-if 和 v-for 同時出現在一個標簽,過濾一個列表中的項目,比如:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
在這種情況下,請將 users 替換為一個計算屬性,讓其返回過濾后的列表。
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
5.1、組件化的定義
組件是獨立和可復用的代碼組織單元,組件系統是 vue 核心特性之一,它使開發者使用小型、獨立和通常可復用的組件構建大型應用。
也可以通俗介紹,把一些用戶在程序中一些獨立的功能和模塊單獨提取出來,然后切分為更小的塊,這些塊有獨立的邏輯,有更好的復用性。
組件按照分類有:頁面組件(導航)、業務組件(登錄)、通用組件(輸入框)。
5.2、組件化特點
vue 的組件是基于配置的,通常編寫的組件是組件配置而非組件,框架后續會生成其構造函數,它們基于 VueComponent 這個類擴展于 vue 。
常見的組件化技術有:prop 屬性、自定義事件、插槽等,這些主要用于組件之間的通信等。
組件之間遵循單向數據流原則。
5.3、組件化的優點
組件化的開發能大幅提高開發效率、測試性和復用性等。
合理的劃分組件能夠大幅提升應用性能,組件應該是高內聚,低耦合的。
vue 組件可能存在多個實例,如果使用對象形式定義 data ,則會導致它們公用一個 data 對象,那么狀態變更將會影響所有組件實例,這是不合理的。
如果采用函數的形式,在實例化組件時,data 會被當做工廠函數返回一個全新的 data 對象,有效規避多實例之間狀態污染問題。
所以在組件中的 data 必須是函數,不能使用對象形式。那為什么 vue 根實例沒有限制呢?
在 vue 中根實例只能有一個,所以不需要擔心多實例的問題,所以根實例中的 data 可以是函數也可以是對象。
我所了解的 vue 性能優化方法分別有:
1>、路由懶加載
Vue.use(VueRouter)
// 傳統寫法
import Home from '@/views/login/index.vue'
//路由懶加載
const Login=()=> import('@/views/login/index.vue')
const router=new VueRouter({
routes: [
{ path: '/login', component: Login },
{ path: '/home', component: Home },
]
export default router
使用路由懶加載,項目打包的時候體積會大幅減小,訪問項目時,這些組件也會按需進行加載,大大提升了項目性能。
2>、keep-alive 緩存頁面
<template>
<keep-alive>
<router-view />
</keep-alive>
</template>
使用 keep-alive 之后會緩存頁面,第一次加載之后,關閉再次打開,頁面不會重新渲染。keep-alive 的屬性:
3>、v-for遍歷避免同時使用 v-if
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
4>、長列表性能優化
如果列表是純粹的數據展示,不會有任何的改變,就不需要做響應式。
export default{
data(){
return {
users:[]
}
},
created(){
const user=await axios("/api/user")
this.users=Object.freeze(user)
}
}
Object.freeze() 方法可以凍結一個對象,對象被凍結之后不能被修改,可以讓性能大幅度提升。
如果是大數據長列表,可采用虛擬滾動,只渲染少部分區域的內容。可采用三方 vue-virtual-scroll。
5>、事件的銷毀
vue組件銷毀時,會自動解綁它的全部指令及事件監聽器,但是僅限于組件本身的事件。
created(){
this.timer=setInterval( this.refresh, 2000 )
},
beforeDestory(){
clearInterval( this.timer )
}
6>、圖片懶加載
對于圖片過多的頁面,為了加快頁面的加載速度,所以很多時候,需要把未出現在可視區域的圖片暫不進行加載,滾動到可視區域之后再開始加載。
可以使用三方的 vue-lazyload 庫。
<img v-lazy="/src/img/01.jpg" />
7>、第三方插件按需引用
使用三方庫時,可以按需引入避免體積太大。比如 element-ui :
import { Button } from "element-ui"
8>、無狀態的組件標記為函數式組件
<template functional>
<div>組件內容</div>
</template>
通過 functional 將組件標記為函數式組件,因為函數式組件沒有實例,所以運行時耗費資源較少。
另外還有 v-show 復用 DOM、子組件分割、SSR 等。
computed VS methods
computed:{
yyds(){
log("computed show")
return "計算屬性"
}
},
methods:{
show(){
log("method show")
return "計算屬性"
}
}
computed 是計算屬性,methods 內都是方法,所以調用不同分別為:
<div>yyds</div>
<div>show()</div>
computed 是有緩存的,而 methods 沒有緩存,所以 computed 性能比 methods 的好。
computed VS watch
computed 是計算某一個屬性的改變,如果某一個值改變了,計算屬性會監測到,然后進行返回值。
watch 是監聽某一個數據或路由,改變了才會響應,只有改變了才會執行操作。
1.diff算法是虛擬DOM技術的必然產物:通過新舊虛擬DOM作對比(即diff),將變化的地方更新在真實DOM上;
另外,也需要diff高效的執行對比過程,從而降低時間復雜度為O(n)。(what)
2.vue2.x中為了降低Watcher粒度,每個組件只有一個Watcher與之對應,只有引入diff才能精確找到發生變化的地方。(why)
3.vue中diff執行的時刻是組件實例執行其更新函數時,它會比對上一次渲染結果oldVnode和新的渲染結果newVnode,此過程稱為patch。(where)
4.diff過程整體遵循深度優先、同層比較的策略;兩個節點之間比較會根據它們是否擁有子節點或者文本節點做不同操作;(How)
比較兩組子節點是算法的重點,首先假設頭尾節點可能相同做4次比對嘗試,如果沒有找到相同節點才按照通用方式遍歷查找,查找結束再按情況處理剩下的節點;
借助key通常可以非常精確找到相同節點,因此整個patch過程非常高效。
vue組件內數據相關的屬性它們的樣式優先級從高到底分別為:
props > methods > data > computed > watch
vue 組件之間的關系有:父子關系、兄弟關系、隔代關系。
所以 vue 組件之間的通信可分為:父子組件之間通信,兄弟組件之間通信和跨層組件之間通信。
1>、父傳子
可使用的方法有:
2>、子傳父
可使用的方法:
3>、兄弟組件之間
4>、跨層組件
5>、沒有關系的組件之間通信
前端事件循環指的是 JavaScript 在瀏覽器中運行時的一種機制,它用于處理異步事件。事件循環會不斷檢查是否有事件發生,并調用相應的回調函數來處理這些事件。
事件循環的工作流程如下:
事件循環的優點:
事件循環的注意點:
瀏覽器事件循環與 Node.js 事件循環
Node.js 和瀏覽器都使用事件循環來處理異步事件。但是,兩者之間存在一些關鍵差異:
1. 事件隊列
2. 任務執行順序
3. 事件循環的階段
4. timers
5. nextTick
Node.js 是一個基于 JavaScript 的運行時環境,用于構建服務器端應用程序。libuv 是一個開源的 C 庫,提供了一組異步 I/O 和事件處理 API。
Node.js 與 libuv 的關系:
libuv 的主要功能:
Node.js 使用 libuv 的優勢:
事件循環是 libuv 的核心,也是理解 libuv 的關鍵。事件循環是一種處理事件的機制,它會不斷檢查是否有事件發生,并調用相應的回調函數來處理這些事件。
libuv 的事件循環是基于 epoll 的,epoll 是 Linux 內核中的一種高效事件通知機制。epoll 可以監視大量的文件描述符,并通知應用程序哪些文件描述符上有事件發生。
libuv 的事件循環主要包含以下幾個部分:
事件循環的工作流程如下:
libuv 提供了以下幾種事件類型:
libuv 的事件循環具有以下優點:
以下是一些 libuv 事件循環的示例代碼:
static void uv__poll(uv_loop_t *loop) {
int timeout=loop->time < 0 ? -1 : loop->time;
int nfds=epoll_wait(loop->epoll_fd, loop->watchers, ARRAY_SIZE(loop->watchers), timeout);
if (nfds < 0) {
if (errno !=EAGAIN && errno !=EINTR) {
uv__set_error(loop, errno);
}
return;
}
for (i=0; i < nfds; i++) {
uv_watcher_t *w=loop->watchers[i].data;
if (w->events & UV_READABLE) {
w->cb(w, UV_READABLE, 0);
}
if (w->events & UV_WRITABLE) {
w->cb(w, UV_WRITABLE, 0);
}
}
}
這段代碼是 libuv 的 uv__poll() 函數,用于處理 I/O 事件。
libuv 的事件循環是一種高效、可擴展和跨平臺的事件處理機制。理解 libuv 的事件循環對于理解 libuv 的工作原理至關重要。
Beautiful Soup 是一個可以從 HTML 或 XML 文件中提取數據的 Python 庫,它提供了一些簡單的操作方式來幫助你處理文檔導航,查找,修改文檔等繁瑣的工作。因為使用簡單,所以 Beautiful Soup 會幫你節省不少的工作時間。
你可以使用如下命令安裝 Beautiful Soup。二選一即可。
$ easy_install beautifulsoup4
$ pip install beautifulsoup4
Beautiful Soup 不僅支持 Python 標準庫中的 HTML 解析器,還支持很多第三方的解析器,比如 lxml,html5lib 等。初始化 Beautiful Soup 對象時如果不指定解析器,那么 Beautiful Soup 將會選擇最合適的解析器(前提是你的機器安裝了該解析器)來解析文檔,當然你也可以手動指定解析器。這里推薦大家使用 lxml 解析器,功能強大,方便快捷,而且該解析器是唯一支持 XML 的解析器。
你可以使用如下命令來安裝 lxml 解析器。二選一即可。
$ easy_install lxml
$ pip install lxml
Beautiful Soup 使用來起來非常簡單,你只需要傳入一個文件操作符或者一段文本即可得到一個構建完成的文檔對象,有了該對象之后,就可以對該文檔做一些我們想做的操作了。而傳入的文本大都是通過爬蟲爬取過來的,所以 Beautiful Soup 和 requests 庫結合使用體驗更佳。
# demo 1
from bs4 import BeautifulSoup
# soup=BeautifulSoup(open("index.html"))
soup=BeautifulSoup("<html><head><title>index</title></head><body>content</body></html>", "lxml") # 指定解析器
print(soup.head)
# 輸出結果
<head><title>index</title></head>
Beautiful Soup 將復雜的 HTML 文檔轉換成一個復雜的樹形結構,每個節點都是 Python 對象,所有對象可以歸納為 4 種: Tag,NavigableString,BeautifulSoup,Comment。
Tag 就是 HTML 的一個標簽,比如 div,p 標簽等,也是我們用的最多的一個對象。
NavigableString 指標簽內部的文字,直譯就是可遍歷的字符串。
BeautifulSoup 指一個文檔的全部內容,可以當成一個 Tag 來處理。
Comment 是一個特殊的 NavigableString,其輸出內容不包括注視內容。
為了故事的順利發展,我們先定義一串 HTML 文本,下文的所有例子都是基于這段文本的。
html_doc="""
<html><head><title>index</title></head>
<body>
<p class="title"><b>首頁</b></p>
<p class="main">我常用的網站
<a href="https://www.google.com" class="website" id="google">Google</a>
<a href="https://www.baidu.com" class="website" id="baidu">Baidu</a>
<a href="https://cn.bing.com" class="website" id="bing">Bing</a>
</p>
<div><!--這是注釋內容--></div>
<p class="content1">...</p>
<p class="content2">...</p>
</body>
"""
Tag 有兩個很重要的屬性,name 和 attributes。期中 name 就是標簽的名字,attributes 是標簽屬性。標簽的名字和屬性是可以被修改的,注意,這種修改會直接改變 BeautifulSoup 對象。
# demo 2
soup=BeautifulSoup(html_doc, "lxml");
p_tag=soup.p
print(p_tag.name)
print(p_tag["class"])
print(p_tag.attrs)
p_tag.name="myTag" # attrs 同樣可被修改,操作同字典
print(p_tag)
#輸出結果
p
['title']
{'class': ['title']}
<myTag class="title"><b>首頁</b></myTag>
由以上例子我么可以看出,可以直接通過點屬性的方法來獲取 Tag,但是這種方法只能獲取第一個標簽。同時我們可以多次調用點屬性這個方法,來獲取更深層次的標簽。
# demo 3
soup=BeautifulSoup(html_doc, "lxml");
print(soup.p.b)
#輸出結果
<b>首頁</b>
如果想獲得所有的某個名字的標簽,則可以使用 find_all(tag_name) 函數。
# demo 4
soup=BeautifulSoup(html_doc, "lxml");
a_tags=soup.find_all("a")
print(a_tags)
#輸出結果
[<a class="website" href="https://www.google.com" id="google">Google</a>, <a class="website" href="https://www.baidu.com" id="baidu">Baidu</a>, <a class="website" href="https://cn.bing.com" id="bing">Bing</a>]
我們可以使用 .contents 將 tag 以列表方式輸出,即將 tag 的子節點格式化為列表,這很有用,意味著可以通過下標進行訪問指定節點。同時我們還可以通過 .children 生成器對節點的子節點進行遍歷。
# demo 5
soup=BeautifulSoup(html_doc, "lxml");
head_tag=soup.head
print(head_tag)
print(head_tag.contents)
for child in head_tag.children:
print("child is : ", child)
#輸出結果
<head><title>index</title></head>
[<title>index</title>]
child is : <title>index</title>
.children 只可以獲取 tag 的直接節點,而獲取不到子孫節點,.descendants 可以滿足你。
# demo 6
soup=BeautifulSoup(html_doc, "lxml");
head_tag=soup.head
for child in head_tag.descendants:
print("child is : ", child)
# 輸出結果
child is : <title>index</title>
child is : index
通過 .parent 屬性獲取標簽的父親節點。 title 的父標簽是 head,html 的父標簽是 BeautifulSoup 對象,而 BeautifulSoup 對象的父標簽是 None。
# demo 7
soup=BeautifulSoup(html_doc, "lxml");
title_tag=soup.title
print(title_tag.parent)
print(type(soup.html.parent))
print(soup.parent)
# 輸出結果
<head><title>index</title></head>
<class 'bs4.BeautifulSoup'>
None
同時,我們可以通過 parents 得到指定標簽的所有父親標簽。
# demo 8
soup=BeautifulSoup(html_doc, "lxml");
a_tag=soup.a
for parent in a_tag.parents:
print(parent.name)
# 輸出結果
p
body
html
[document]
通過 .next_sibling 和 .previous_sibling 來獲取下一個標簽和上一個標簽。
# demo 9
soup=BeautifulSoup(html_doc, "lxml");
div_tag=soup.div
print(div_tag.next_sibling)
print(div_tag.next_sibling.next_sibling)
# 輸出結果
<p class="content1">...</p>
你可能會納悶,調用了兩次 next_sibling 怎么只有一個輸出呢,這方法是不是有 bug 啊。事實上是 div 的第一個 next_sibling 是div 和 p 之間的換行符。這個規則對于 previous_sibling 同樣適用。
另外,我們可以通過 .next_siblings 和 .previous_siblings 屬性可以對當前節點的兄弟節點迭代輸出。在該例子中,我們在每次輸出前加了前綴,這樣就可以更直觀的看到 dib 的第一個 previous_sibling 是換行符了。
# demo 10
soup=BeautifulSoup(html_doc, "lxml");
div_tag=soup.div
for pre_tag in div_tag.previous_siblings:
print("pre_tag is : ", pre_tag)
# 輸出結果
pre_tag is :
pre_tag is : <p class="main">我常用的網站
<a class="website" href="https://www.google.com" id="google">Google</a>
<a class="website" href="https://www.baidu.com" id="baidu">Baidu</a>
<a class="website" href="https://cn.bing.com" id="bing">Bing</a>
</p>
pre_tag is :
pre_tag is : <p class="title"><b>首頁</b></p>
pre_tag is :
通過 .next_element 和 .previous_element 獲取指定標簽的前一個或者后一個被解析的對象,注意這個和兄弟節點是有所不同的,兄弟節點是指有相同父親節點的子節點,而這個前一個或者后一個是按照文檔的解析順序來計算的。
比如在我們的文本 html_doc 中,head 的兄弟節點是 body(不考慮換行符),因為他們具有共同的父節點 html,但是 head 的下一個節點是 title。即soup.head.next_sibling=title soup.head.next_element=title
# demo 11
soup=BeautifulSoup(html_doc, "lxml");
head_tag=soup.head
print(head_tag.next_element)
title_tag=soup.title
print(title_tag.next_element)
# 輸出結果
<title>index</title>
index
同時這里還需要注意的是 title 下一個解析的標簽不是 body,而是 title 標簽內的內容,因為 html 的解析順序是打開 title 標簽,然后解析內容,最后關閉 title 標簽。
另外,我們同樣可以通過 .next_elements 和 .previous_elements 來迭代文檔樹。由遺下例子我們可以看出,換行符同樣會占用解析順序,與迭代兄弟節點效果一致。
# demo 12
soup=BeautifulSoup(html_doc, "lxml");
div_tag=soup.div
for next_element in div_tag.next_elements:
print("next_element is : ", next_element)
# 輸出結果
next_element is : 這是注釋內容
next_element is :
next_element is : <p class="content1">...</p>
next_element is : ...
next_element is :
next_element is : <p class="content2">...</p>
next_element is : ...
next_element is :
next_element is :
本章節介紹了 Beautiful Soup 的使用場景以及操作文檔樹節點的基本操作,看似很多東西其實是有規律可循的,比如函數的命名,兄弟節點或者下一個節點的迭代函數都是獲取單個節點函數的復數形式。
同時由于 HTML 或者 XML 這種循環嵌套的復雜文檔結構,致使操作起來甚是麻煩,掌握了本文對節點的基本操作,將有助于提高你寫爬蟲程序的效率。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。