目錄
HTTP協議
HTTP請求:
HTTP響應:
會話與會話狀態:
Cookie
Session
Cookie和Session的區別
HTTP協議
HTTP請求:
Post /test.php HTTP/1.1 //請求行以一個方法符號開頭,以空格分開,后面跟著請求的URI和協議的版本
Host: www.test.com //請求頭
User-agent:mozilla/5.0(windows NT 6.1: rv: 15.0)
Gecko/20100101 firefox15.0
//空白行,代表請求頭結束
Username=admin&passwd=admin //請求正文
HTTP請求方法
GET 請求獲取Request-URI所標識的資源
POST 在Request-URI所標識的資源后附加新的數據
HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT 請求服務器存儲一個資源,并用Request-URI作為其標識
常用的為GET和POST;GET和POST的區別:
GET提交的內容會直接顯示在URL中,私密性較差,可以用于顯示一些公共資源;但是GET效率會比較高。
POST不會將內容顯示在URL中,可以用于提交一些敏感數據,例如用戶名或密碼。
HTTP響應:
HTTP/1.1 200 OK //響應行由協議版本號,響應狀態碼和文本描述組成
Data:sun,15 nov 2018 11:02:04 GMT //響應頭
Server:bfe/1.0.8.9
……
Connection: keep-alive
//空白行,代表響應頭結束
<html>
</html><title>index.heml</title> //響應正文
HTTP的狀態碼:
狀態代碼由三位數字組成,第一個數字定義了響應的類別,且有五種可能取值。
1xx:指示信息 —— 表示請求已接收,繼續處理。
2xx:成功 —— 表示請求已被成功接收、理解、接受。
3xx:重定向 —— 要完成請求必須進行更進一步的操作。
4xx:客戶端錯誤 —— 請求有語法錯誤或請求無法實現。
5xx:服務器端錯誤 —— 服務器未能實現合法的請求。
常見狀態代碼、狀態描述的說明如下。
200 OK:客戶端請求成功。
400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解。
401 Unauthorized:請求未經授權,這個狀態代碼必須和 WWW-Authenticate 報頭域一起使用。
403 Forbidden:服務器收到請求,但是拒絕提供服務。
404 Not Found:請求資源不存在,舉個例子:輸入了錯誤的URL。
500 Internal Server Error:服務器發生不可預期的錯誤。
503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間后可能恢復正常。
會話與會話狀態:
Web中的會話是指一個客戶端瀏覽器與web服務器之間連續發生一系列請求和響應過程。會話狀態是指在會話過程中產生的狀態信息;借助會話狀態,web服務器能夠把屬于同一會話中的一系列的請求和響應關聯起來。
Cookie
概述
Cookie是一種在客戶端保持HTTP狀態信息的技術,它好比商場發放的優惠卡。在瀏覽器訪問Web服務器的某個資源時,由Web服務器在在HTTP響應頭中附帶傳送給瀏覽器一片數據,web服務器傳送給各個客戶端瀏覽器的數據是可以各不相同的。
一旦Web瀏覽器保存了某個Cookie,那么它在以后每次訪問該Web服務器是都應在HTTP請求頭中將這個Cookie回傳個Web服務器。Web服務器通過在HTTP響應消息中增加Set-Cookie響應頭字段將CooKie信息發送給瀏覽器,瀏覽器則通過在HTTP請求消息中增加Cookie請求頭字段將Cookie回傳給Web服務器。
一個Cookie只能標識一種信息,它至少含有一個標識該消息的名稱(NAME)和和設置值(VALUE)。一個Web瀏覽器也可以存儲多個Web站點提供的Cookie。瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。
傳送示意圖
特點
存儲于瀏覽器頭部/傳輸與HTTP頭部,寫時帶屬性,讀時無屬性。由三元【name,domain,path】唯一確定Cookie。
Set-Cookie2響應頭字段
Set-Cookie2頭字段用于指定WEB服務器向客戶端傳送的Cookie內容,但是按照Netscape規范實現Cookie功能的WEB服務器, 使用的是Set-Cookie頭字段,兩者的語法和作用類似。Set-Cookie2頭字段中設置的cookie內容是具有一定格式的字符串,它必須以Cookie的名稱和設置值開頭,格式為"名稱=值”,后面可以加上0個或多個以分號(;) 和空格分隔的其它可選屬性,屬性格式一般為 "屬性名=值”。
除了“名稱=值”對必須位于最前面外,其他的可選屬性可以任意。Cookie的名稱只能由普通的英文ASCII字符組成,瀏覽器不用關心和理解Cookie的值部分的意義和格式,只要WEB服務器能理解值部分的意義就行。大多數現有的WEB服務器都是采用某種編碼方式將值部分的內容編碼成可打印的ASCII字符,RFC 2965規范中沒有明確限定編碼方式。
舉例: Set-Cookie2: user-hello; Version=1; Path=/
Cookie請求頭字段
Cookie請求頭字段中的每個Cookie之間用逗號(,)或分號(;)分隔。在Cookie請求字段中除了必須有“名稱=值”的設置外,還可以有Version、path、domain、port等屬性;在Version、path、domain、port等屬性名之前,都要增加一個“$”字符作為前綴。Version屬性只能出現一次,且要位于Cookie請求頭字段設置值的最前面,如果需要設置某個Cookie信息的Path、Domain、Port等屬性,它們必須位于該Cookie信息的“名稱=值”設置之后。
瀏覽器使用Cookie請求頭字段將Cookie信息會送給Web服務器;多個Cookie信息通過一個Cookie請求頭字段會送給Web服務器。
瀏覽器會根據下面幾個規則決定是否發送某個Cookie信息:
1、請求主機名是否與某個存儲的Cookie的Domain屬性匹配
2、請求的端口號是否在該Cookie的Port屬性列表中
3、請求的資源路徑是否在該Cookie的Path屬性指定的目錄及子目錄中
4、該Cookie的有效期是否已過
Path屬性的指向子目錄的Cookie排在Path屬性指向父目錄的Cookie之前
舉例: Cookie: $Version=1; Course=Java; $Path=/hello/lesson;Course=vc; $Path=/hello
Cookie的安全屬性
secure屬性
當設置為true時,表示創建的Cookie會被以安全的形式向服務器傳輸,也就是只能在HTTPS連接中被瀏覽器傳遞到服務器端進行會話驗證,如果是HTTP連接則不會傳遞該信息,所以不會被竊取到Cookie的具體內容。
HttpOnly屬性
如果在Cookie中設置了"HttpOnly"屬性,那么通過程序(JS腳本、Applet等)將無法讀取到Cookie信息,這樣能有效的防止XSS攻擊。
總結:secure屬性 是防止信息在傳遞的過程中被監聽捕獲后信息泄漏,HttpOnly屬性的目的是防止程序獲取cookie后進行攻擊這兩個屬性并不能解決cookie在本機出現的信息泄漏的問題(FireFox的插件FireBug能直接看到cookie的相關信息)。
Session
使用Cookie和附加URL參數都可以將上一-次請求的狀態信息傳遞到下一次請求中,但是如果傳遞的狀態信息較多,將極大降低網絡傳輸效率和增大服務器端程序處理的難度。
概述
Session技術是一種將會話狀態保存在服務器端的技術,它可以比喻成是醫院發放給病人的病歷卡和醫院為每個病人保留的病歷檔案的結合方式??蛻舳诵枰邮?、記憶和回送Session的會話標識號,Session可以且通常是借助Cookie來傳遞會話標識號。
Session的跟蹤機制
HttpSession對象是保持會話狀態信息的存儲結構,一個客戶端在WEB服務器端對應一個各自的HttpSession對象。WEB服務器并不會在客戶端開始訪問它時就創建HttpSession對象,只有客戶端訪問某個能與客戶端開啟會話的服務端程序時,WEB應用程序才會創建一個與該客戶端對應的HttpSession對象。WEB服務器為HttpSession對象分配一個獨一無的會話標識號, 然后在響應消息中將這個會話標識號傳遞給客戶端??蛻舳诵枰涀挊俗R號,并在后續的每次訪問請求中都把這個會話標識號傳送給WEB服務器,WEB服務器端程序依據回傳的會話標識號就知道這次請求是哪個客戶端發出的,從而選擇與之對應的HttpSession對象。
WEB應用程序創建了與某個客戶端對應的HttpSession對象后,只要沒有超出一個限定的空閑時間段,HttpSession對象就駐留在WEB服務器內存之中,該客戶端此后訪問任意的Servlet程序時,它們都使用與客戶端對應的那個已存在的HttpSession對象。
Session是實現網上商城的購物車的最佳方案,存儲在某個客戶Session中的一個集合對象就可充當該客戶的一個購物車。
超時管理
WEB服務器無法判斷當前的客戶端瀏覽器是否還會繼續訪問,也無法檢測客戶端瀏覽器是否關閉,所以,即使客戶已經離開或關閉了瀏覽器,WEB服務器還要保留與之對應的HttpSession對象。隨著時間的推移而不斷增加新的訪問客戶端,WEB服務器內存中將會因此積累起大量的不再被使用的HttpSession對象,并將最終導致服務器內存耗盡。WEB服務器采用“超時限制”的辦法來判斷客戶端是否還在繼續訪問如果某個客戶端在一定的時間之 內沒有發出后續請求,WEB服務器則認為客戶端已經停止了活動,結束與該客戶端的會話并將與之對應的HttpSession對象變成垃圾。
如果客戶端瀏覽器超時后再次發出訪問請求,Web服務器則認為這是一個新的會話開始,將為之創建新的Httpsession對象和分配新的會話標識號。
利用Cookie實現Session的跟蹤
如果WEB服務器處理某個訪問請求時創建了新的HttpSession對象,它將把會話標識號作為一個Cookie項加入到響應消息中,通常情況下,瀏覽器在隨后發出的訪問請求中又將會話標識號以Cookie的形式回傳給WEB服務器。WEB服務器端程序依據回傳的會話標識號就知道以前已經為該客戶端創建了HttpSession對象,不必再為該客戶端創建新的HttpSession對象,而是直接使用與該會話標識號匹配的HttpSession對象,通過這種方式就實現了對同一個客戶端的會話狀態的跟蹤。
利用URL重寫實現Session跟蹤
Servlet規范中引入了一種補充的會話管理機制,它允許不支持Cookie的瀏覽器也可以與WEB服務器保持連續的會話。這種補充機制要求在響應消息的實體內容中必須包含下一 次請求的超鏈接,并將會話標識號作為超鏈接的URL地址的一個特殊參數。將會話標識號以參數形式附加在超鏈接的URL地址后面的技術稱為URL重寫。 如果在瀏覽器不支持Cookie或者關閉了Cookie功能的情況下,WEB服務器還要能夠與瀏覽器實現有狀態的會話,就必須對所有能被客戶端訪問的請求路徑(包括超鏈接、form表單的action屬性設置和重定向的URL)進行URL重寫。
Cookie和Session的區別
session和cookies同樣都是針對單獨用戶的變量(或者說是對象好像更合適點),不同的用戶在訪問網站的時候都會擁有各自的session或者cookies,不同用戶之間互不干擾。
他們的不同點是:
1,存儲位置不同
session在服務器端存儲,比較安全,但是如果session較多則會影響性能
cookies在客戶端存儲,存在較大的安全隱患
2,生命周期不同
session生命周期在指定的時間(如20分鐘) 到了之后會結束,不到指定的時間,也會隨著瀏覽器進程的結束而結束。
cookies默認情況下也隨著瀏覽器進程結束而結束,但如果手動指定時間,則不受瀏覽器進程結束的影響。
總結:簡而言之,兩者都是保存了用戶操作的歷史信息,但是存在的地方不同;而且session和cookie的目的相同,都是為了克服HTTP協議無狀態的缺陷,但是完成方法不同。Session通過cookie在客戶端保存session id,將用戶的其他會話消息保存在服務端的session對象中;而cookie需要將所有信息都保存在客戶端,因此存在著一定的安全隱患,例如本地Cookie中可能保存著用戶名和密碼,容易泄露。
————————————————
版權聲明:本文為CSDN博主「悲觀的樂觀主義者」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_43997530/article/details/105650267
性能優化(網絡方向)
web應用無非是兩臺主機之間互相傳輸數據包的一個過程; 如何減少傳輸過程的耗時就是網絡方向優化的重點, 優化出發點從第一篇文章中說起
DNS解析過程的優化
當瀏覽器從第三方服務跨域請求資源的時候,在瀏覽器發起請求之前,這個第三方的跨域域名需要被解析為一個IP地址,這個過程就是DNS解析;
DNS緩存可以用來減少這個過程的耗時,DNS解析可能會增加請求的延遲,對于那些需要請求許多第三方的資源的網站而言,DNS解析的耗時延遲可能會大大降低網頁加載性能。
dns-prefetch
當站點引用跨域域上的資源時,都應在<head>元素中放置dns-prefetch提示,但是要記住一些注意事項。首先,dns-prefetch僅對跨域域上的DNS查找有效,因此請避免將其用于您當前訪問的站點
<link rel="dns-prefetch" >
preconnect
由于dns-prefetch僅執行DNS查找,但preconnect會建立與服務器的連接。如果站點是通過HTTPS服務的,則此過程包括DNS解析,建立TCP連接以及執行TLS握手。將兩者結合起來可提供機會,進一步減少跨源請求的感知延遲
<!-- 注意順序, precontent和dns-prefetch的兼容性 -->
<link rel="preconnect" crossorigin>
<link rel="dns-prefetch" >
TCP傳輸階段優化
這個前端方面好像能做的有限, 我們都知道 http協議 是基于 tcp的;
升級http協議版本可以考慮下, 比如把 http/1.0 -> http/1.1 -> http/2;
這個需要我們在應用服務器上配置(nginx, Apache等), 不做概述了, 另外還需要客戶端和服務器都支持哦, 目前還沒開發出穩定版本,好多只支持https,不過也不遠了...
http2 的優勢
# 1.多路復用: 同一個tcp連接傳輸多個資源
這樣可以突破統一域名下只允許有限個tcp同時連接,
這樣http1.1所做的減少請求數優化就沒有太大必要了
如多張小圖合成一張大圖(雪碧圖),合并js和css文件
# 2.報文頭壓縮和二進制編碼: 減少傳輸體積
http1 中第一次請求有完整的http報文頭部,第二次請求的也是;
http2 中第一次請求有完整的http報文頭部,第二次請求只會攜帶 path 字段;
這樣就大大減少了發送的量。這個的實現要求客戶端和服務同時維護一個報文頭表。
# 3.Server Push
http2可以讓服務先把其它很可能客戶端會請求的資源(比如圖片)先push發給你,
不用等到請求的時候再發送,這樣可以提高頁面整體的加載速度
但目前支持性不太好...emm...
總的來說, 在 c 端業務下不會太普及, 畢竟需要軟件支持才行...
http 請求響應階段優化
為了讓數據包傳輸的更快, 我們可以從兩個方面入手: 請求的數據包大小(服務器), 請求數據包的頻率(客戶端)
減少請求文件的大小
請求文件對應的是我們項目完成后,打包所指的靜態資源文件(會被部署到服務器), 文件越小, 傳輸的數據包也會相對較小, 講道理也會更快到達客戶端
how to reduce a package size?
目前我們都會使用打包工具了(比如webpack, rollup, glup 等), 如何使用工具來減小包的體積呢? 這邊建議您去官網文檔呢...當然這里列舉一下常用的手段(webpack 的), 但是注意要插件版本更新哦
JS文件壓縮
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
plugins: [
new UglifyJsPlugin({
// 允許并發
parallel: true,
// 開啟緩存
cache: true,
compress: {
// 刪除所有的console語句
drop_console: true,
// 把使用多次的靜態值自動定義為變量
reduce_vars: true,
},
output: {
// 不保留注釋
comment: false,
// 使輸出的代碼盡可能緊湊
beautify: false
}
})
]
}
CSS 文件壓縮
// optimize-css-assets-webpack-plugin
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
}),
];
html 文件壓縮
// html-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false,
},
}),
];
source map 文件關閉
tree shaking
1.代碼不會被執行,不可到達,比如 if(false){// 這里邊的代碼}
2.代碼執行的結果不會被用到
3.代碼只會影響死變量(只寫不讀)
4.方法不能有副作用
// 原理相關: 以后在研究
利用 ES6 模塊的特點:
只能作為模塊頂層的語句出現
import 的模塊名只能是字符串常量
import binding 是 immutable 的
代碼擦除: uglify 階段刪除無用代碼
scope hoisting(作用域提升)
分析出模塊之間的依賴關系,盡可能的把打散的模塊合并到一個函數中去,但前提是不能造成代碼冗余
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
resolve: {
// 針對 Npm 中的第三方模塊優先采用 jsnext:main 中指向的 ES6 模塊化語法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 開啟 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
項目中使用按需加載,懶加載(路由,組件級)
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import(/* webpackChunkName: "foo" */ './Foo.vue') }
{ path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './Bar.vue') }
]
})
開啟 gizp 壓縮
有時候啟用也會消耗服務器性能, 看情況使用吧
暫時先提這么些吧...后續想到了再加
減少請求頻率
因為同一域名下 tcp 連接數的限制導致過多的請求會排隊阻塞, 所以我們需要盡量控制請求的數量和頻率
常見措施
將靜態資源的內聯到HTML中
這樣這些資源無需從服務器獲取, 但可能影響到渲染進程...
<!-- 1.小圖片內聯 base64 (url-loader) -->
<!-- 2.css內聯 -->
<!-- 3.js內聯 -->
<script>
${require('raw-loader!babel-loader!./node_modules/lib-flexible/flexible.js')}
</script>
利用各級緩存(下一篇存儲方面介紹)
通常都是在服務端做相關配置, 但你要知道
我們可以利用http緩存(瀏覽器端)來減少和攔截二次請求, 當然一般都是在服務端設置的;
服務器端也可以設置緩存(redis等), 減少數據查詢的時間同樣可以縮短整個請求時間
利用本地存儲
我們可以將常用不變的信息存在本地(cookie,storage API 等);
判斷存在就不去請求相關的接口, 或者定期去請求也是可以的
花錢買 CDN 加速
CDN 又叫內容分發網絡,通過把資源部署到世界各地,用戶在訪問時按照就近原則從離用戶最近的服務器獲取資源,從而加速資源的獲取速度。 CDN 其實是通過優化物理鏈路層傳輸過程中的網速有限、丟包等問題來提升網速的...
購買 cdn 服務器;
然后把網頁的靜態資源上傳到 CDN 服務上去,
在請求這些靜態資源的時候需要通過 CDN 服務提供的 URL 地址去訪問;
# 注意, cdn 緩存導致的新版本發布后不生效的問題
所以打包的時候常在文件后面加上 hash 值
然后在 HTML 文件中的資源引入地址也需要換成 CDN 服務提供的地址
/alicdn/xx12dsa311.js
# 利用不同域名的 cdn 去存放資源, (tcp連接限制)
webpack 構建時添加 cdn
// 靜態資源的導入 URL 需要變成指向 CDN 服務的絕對路徑的 URL 而不是相對于 HTML 文件的 URL。
// 靜態資源的文件名稱需要帶上有文件內容算出來的 Hash 值,以防止被緩存。
// 不同類型的資源放到不同域名的 CDN 服務上去,以防止資源的并行加載被阻塞。
module.exports = {
// 省略 entry 配置...
output: {
// 給輸出的 JavaScript 文件名稱加上 Hash 值
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
// 指定存放 JavaScript 文件的 CDN 目錄 URL
publicPath: '//js.cdn.com/id/',
},
module: {
rules: [
{
// 增加對 CSS 文件的支持
test: /\.css$/,
// 提取出 Chunk 中的 CSS 代碼到單獨的文件中
use: ExtractTextPlugin.extract({
// 壓縮 CSS 代碼
use: ['css-loader?minimize'],
// 指定存放 CSS 中導入的資源(例如圖片)的 CDN 目錄 URL
publicPath: '//img.cdn.com/id/'
}),
},
{
// 增加對 PNG 文件的支持
test: /\.png$/,
// 給輸出的 PNG 文件名稱加上 Hash 值
use: ['file-loader?name=[name]_[hash:8].[ext]'],
},
// 省略其它 Loader 配置...
]
},
plugins: [
// 使用 WebPlugin 自動生成 HTML
new WebPlugin({
// HTML 模版文件所在的文件路徑
template: './template.html',
// 輸出的 HTML 的文件名稱
filename: 'index.html',
// 指定存放 CSS 文件的 CDN 目錄 URL
stylePublicPath: '//css.cdn.com/id/',
}),
new ExtractTextPlugin({
// 給輸出的 CSS 文件名稱加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
// 省略代碼壓縮插件配置...
],
};
/*
以上代碼中最核心的部分是通過 publicPath 參數設置存放靜態資源的 CDN 目錄 URL,
為了讓不同類型的資源輸出到不同的 CDN,需要分別在:
output.publicPath 中設置 JavaScript 的地址。
css-loader.publicPath 中設置被 CSS 導入的資源的的地址。
WebPlugin.stylePublicPath 中設置 CSS 文件的地址。
設置好 publicPath 后,WebPlugin 在生成 HTML 文件和 css-loader 轉換 CSS 代碼時,會考慮到配置中的 publicPath,用對應的線上地址替換原來的相對地址。
*/
參考
DNS MDN]
webpack 文檔
深入淺出 Webpack
Scope Hoisting
在了解了javascript的語言基礎和特性后
javascript真正大放光彩的地方來了——這就是javascript DOM
Javascript DOM
DOM(Document Object Model),文檔對象模型。
是W3C組織推薦的處理可擴展標記語言(HTML或者XML)的標準編程接口;W3C已經定義了一系列DOM接口,通過這些DOM接口可以改變網頁的內容、結構和樣式。
簡單的說就是一套操作文檔內容的方法。
需要注意的是,我們需要把DOM當作一個整體,不能分割看待,即DOM(文檔對象模型)是一套操作文檔內容的方法。
DOM把以上內容看作都是對象
<!DOCTYPE html> <html> <head> <title>Shopping list</title> <meta charset="utf-8"> </head> <body> <h1>What to buy</h1> <p id="buy" title="a gentle reminder">Don't forget to buy this stuff</p> <ul id="purchases"> <li>A tin od beans</li> <li>Cheese</li> <li>Milk</li> </ul> </body> </html>
1、獲取DOM四種基本方法
1、getElementById()
2、getElementsByTagname()
3、getAttribute()
4、setAttribute()
常用的兩種解析:
1. getElementById():
參數:元素的ID值。 (元素節點簡稱元素)
返回值:一個有指定ID的元素對象(元素是對象)
注:這個方法是與document對象相關聯,只能由document對象調用。
用法:document.getElementById(Id)
例:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="time">2020-04-16</div> <script> // 1. 因為我們文檔頁面從上往下加載,所以先得有標簽 所以我們script寫到標簽的下面 // 2. get 獲得 element 元素 by 通過 駝峰命名法 // 3. 參數 id是大小寫敏感的字符串 // 4. 返回的是一個元素對象 var timer = document.getElementById('time'); console.log(timer); console.log(typeof timer); // 5. console.dir 打印我們返回的元素對象 更好的查看里面的屬性和方法 console.dir(timer); </script> </body> </html>
看一下控制臺打印的是什么
可以看到 console.log(timer)打印出來的是整個div標簽
timer類型是個對象
2. getElementsByTagName():
參數:元素名
返回值:一個對象數組。這個數組里每個元素都是對象,每個對象分別對應著文檔里給定標簽的一個元素。
注:這個方法可和一般元素關聯。這個方法允許我們把通配符當作它的參數,返回在某份html文檔里總共有多少個元素節點。
用法:element.getElementsByTagName(TagName)
例:
var items=document.getElementsByTagName("li");
items.length;//3
document.getElementsByTagName(“*”);//12
2、事件基礎
3.1 事件概述
JavaScript使我們有能力創建動態頁面,而事件是可以被JavaScript偵測到的行為。
簡單理解:觸發——>響應機制
網頁中每個元素都可以產生某些可以觸發JavaScript的事件,例如,我們可以在用戶點擊某按鈕產生一個事件,然后去執行某些操作
3.2 事件三要素
事件源 、事件類型、事件處理程序,我們也稱為事件三要素
(1) 事件源 事件被觸發的對象 誰
(2) 事件類型 如何觸發 什么事件 比如鼠標點擊(onclick) 還是鼠標經過 還是鍵盤按下
(3) 事件處理程序 通過一個函數賦值的方式 完成
代碼實例
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <button id="btn">唐伯虎</button> <script> // 點擊一個按鈕,彈出對話框 // 1. 事件是有三部分組成 事件源 事件類型 事件處理程序 我們也稱為事件三要素 //(1) 事件源 事件被觸發的對象 誰 按鈕 var btn = document.getElementById('btn'); //(2) 事件類型 如何觸發 什么事件 比如鼠標點擊(onclick) 還是鼠標經過 還是鍵盤按下 //(3) 事件處理程序 通過一個函數賦值的方式 完成 btn.onclick = function() { alert('點秋香'); } </script> </body> </html>
運行結果
1、獲取事件源
2、注冊事件(綁定事件)
3、添加事件處理程序(采取函數賦值形式)
代碼實戰
-
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div>123</div> <script> // 執行事件步驟 // 點擊div 控制臺輸出 我被選中了 // 1. 獲取事件源 var div = document.querySelector('div'); // 2.綁定事件 注冊事件 // div.onclick // 3.添加事件處理程序 div.onclick = function() { console.log('我被選中了'); } </script> </body> </html>
常用的DOM事件
onclick事件---當用戶點擊時執行
onload事件---當用戶進入時執行
onunload事件---用用戶離開時執行
onmouseover事件---當用戶鼠標指針移入時執行
onmouseout事件---當用戶鼠標指針移出時執行
onmousedown事件---當用戶鼠標摁下時執行
onmouseup事件---當用戶鼠標松開時執行
————————————————
版權聲明:本文為CSDN博主「那是我吶」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_42402867/article/details/105567787
文章目錄
繼承性的描述:
繼承性是指被包在內部的標簽將擁有外部標簽的樣式性,即子元素可以繼承父類的屬性。
例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div{ color: blue; } </style> </head> <body> <div>父元素 <div>子元素 <p>我依舊是子元素</p> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> p{ font-size: 32px; } </style> </head> <body> <p style="color: blue;">我這里體現了層疊性呀</p> </body> </html>
使用結論
由于內容有限,但是結論是一定的,所以我直接給出結論:
若多個選擇器定義的樣式不沖突,則元素應用所有選擇器定義的樣式。
若多個選擇器定義的樣式發生沖突(比如:同時定義了字體顏色屬性),則CSS按照選擇器的優先級,讓元素應用優先級搞得選擇器樣式。
CSS定義的選擇器優先級從高到低為:行內樣式–>ID樣式–>類樣式–>標記樣式。
如若想直接定義使用哪個樣式,不考慮優先級的話,則使用!important,把這個加在樣式后面就行了。
優先級
定義CSS樣式時,經常出現兩個或更多規則應用在同一個元素上,這時就會出現優先級的問題。層疊性和選擇器的圈中有很大的關系。
優先級的使用說明
權重分析:
內聯樣式:如:style="",權重為1000。
ID選擇器,如:#content,權重為100。
類,偽類和屬性選擇器,如.content,權重為10。
標簽選擇器和偽元素選擇器,如div p,權重為1。
繼承樣式,權重為0。
將基本選擇器的權重相加之和,就是權重大小,值越大,權重越高。
計算權重方法
數標簽:先數權重最高的標簽,然后數第二高權重的標簽,以此類推,就會生成一個數組,里面包含四個數字。
比如(0,0,0,0)分別對應(行內式個數,id選擇器個數,類選擇器個數,標簽選擇器個數)
然后兩個選擇器通過對別四個數字的大小,確定權重關系。
例:
#box ul li a.cur有1個id標簽,1個類,3個標簽,那么4個0就是(0,1,1,3)
.nav ul .active .cur有0個id,3個類,1個標簽,那么4個0就是(0,0,3,1)
例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .p1{ color: blue; } #p1{ color: red; } </style> </head> <body> <p id="p1" class="p1">我們來試一下優先級</p> </body> </html>
先推測一波,因為前面講到了ID選擇器的權重是大于類選擇器的,所以這里顏色應該為red。
效果如下:
推測正確!優先級GET!
今天講一下使用vant Swipe 輪播控件過程中遇到的問題
主要是使用swiper自定義的大小的時候,寬度適應不同分辨率的移動設備
適應寬度的同時還需控件的正常使用
先看一下需要實現的功能,
一個簡單的輪播圖,但是每個輪播的寬度需要低于100%,使第二個輪播的van-swipe-item可以展示到第一個位置一部分
這時我們再去vant的文檔查看一下控件
剛好有一個自定義控件大小的可以使用,完美解決了我們的問題
當我們使用控件之后
<van-swipe :loop="false" @change="onChange" :width="350"> <van-swipe-item v-bind:id="item0"><div class="swipe0"> <div class="contion"> <p class="title">家中有事,申請請假一天</p> <p class="title1"><span class="rice"></span>部門經理核審中</p> <p class="time">03.8 14.25</p> <p class="type">放假申請</p> </div> <img src="../../assets/images/index/xx/fangjia.png"> </div></van-swipe-item> <van-swipe-item ><div class="swipe1"></div></van-swipe-item> <van-swipe-item ><div class="swipe2"></div></van-swipe-item> <template #indicator> <div class="custom-indicator"> {{ current + 1 }}/3 </div> </template> </van-swipe>
發現功能可以使用,但是再 iPhone8/7 plus 以及iPhone5/se 等分辨率下出現了寬度固定而不適應的情況,
簡單來說,我們把van-swipe-item寬度控制在了80% 第二個van-swipe-item自然可以展示出來一部分
但是當滑到第二頁的時候 由于第一頁的寬度還是80% 所以就出現了這樣的情況,所以我打算采用
動態的改變 滑動到第幾頁的時候 把當頁的寬度變為80% 其他頁保持不變,
于是
<van-swipe :loop="false" @change="onChange" > <van-swipe-item v-bind:id="item0"><div class="swipe0"> <div class="contion"> <p class="title">家中有事,申請請假一天</p> <p class="title1"><span class="rice"></span>部門經理核審中</p> <p class="time">03.8 14.25</p> <p class="type">放假申請</p> </div> <img src="../../assets/images/index/xx/fangjia.png"> </div></van-swipe-item> <van-swipe-item v-bind:id="item1"><div class="swipe1"></div></van-swipe-item> <van-swipe-item v-bind:id="item2"><div class="swipe2"></div></van-swipe-item> <template #indicator> <div class="custom-indicator"> {{ current + 1 }}/3 </div> </template> </van-swipe>
首先 我們為每個swipe-item添加id
data(){ return { android: true, ios: true, iphoneX: true, current: 0, item0:'item0', item1:'item1', item2:'item2', } }, mounted(){ }, methods: { onChange(index){ console.log('當前 Swipe 索引:' + index); if(index==1){ var div =document.getElementById("item0").style.setProperty('width', '10rem', 'important'); var div1 =document.getElementById("item1").style.setProperty('width', '9.3333333rem', 'important'); var div2 =document.getElementById("item2").style.setProperty('width', '9.3333333rem', 'important'); } else if(index==2){ var div1 =document.getElementById("item1").style.setProperty('width', '10rem', 'important'); var div0 =document.getElementById("item0").style.setProperty('width', '10rem', 'important'); var div2 =document.getElementById("item2").style.setProperty('width', '9.3333333rem', 'important'); } else if(index==0){ var div =document.getElementById("item2"); var div0 =document.getElementById("item0").style.setProperty('width', '9.3333333rem', 'important'); var div1 =document.getElementById("item1").style.setProperty('width', '9.3333333rem', 'important'); } },
此外,監聽滑動事件,根據滑動到第幾頁 更改當前頁面的寬度,
這樣就解決了
蘭蘭設計:前端達人
emcc [options] file ...
這個輸入文件file,既可以是clang可以編譯的C/C++語言,也可以是二進制形式的llvm bitcode或者人類可讀形式的llvm assembly文件。
大部分clang或者gcc的選項(option)都是可以工作的,比如:
# 顯示信息 emcc --help # 顯示編譯器版本信息 emcc --version
如果想看當前Emscripten中clang版本支持的全部選項列表,可以直接使用命令:
clang --help.
emcc修改的或者emcc中新的選項列在下面:
首先是一些編譯優化flag,它們-O0,-O1,-O2,-Os,-Oz,-O3。
-O0:
不進行編譯優化(這是默認情況)。當你剛開始移植項目是推薦使用它,因為它會包含許多斷言。
-O1:
簡單優化。推薦你在既想縮短編譯時間又想編譯優化時使用。它畢竟比-O2級別的優化編譯起來快多了。它會進行asm.js和llvm的-O1進行優化,它會relooping,會刪除運行時斷言和C++異常捕獲,它也會使得-s ALIASING_FUNCTION_POINTERS=1。
想要C++異常捕獲重新可用,請設置:-s DISABLE_EXCEPTION_CATCHING=0。
-O2:
和-O1類似,不過多了JavaScript級別的優化以及一些llvm -O3的優化項。當你想發布項目的時候,推薦使用本級別優化。
-O3:
和-O2類似,不過比-O2又多了一些JavaScript優化,而且編譯時間明顯比-O2長。這個也推薦在發布版本的時候使用。
-Os:
和-O3類似,不過增加了額外的優化以減小生成的代碼體積,代價是比-O3性能差一點。-Os優化會同時影響llvm bitcode 和JavaScript文件的生成。
-Oz:
和-Os類似,不過進一步減小了代碼體積。
-s OPTION=VALUE
傳給編譯器的所有涉及到JavaScript代碼生成的選項。選項列表,請見settings.js。
對于某個選項的值,不僅可以直接在emcc命令行里面設定,也可以把他們寫成json文件。比如下面,就是將DEAD_FUNCTIONS選項的值放到了path/to/file文件里,emcc里面傳這個文件的路徑。
-s DEAD_FUNCTIONS=@/path/to/file
note: 1、文件內容可以是:["_func1","_func2"]; 2、文件路徑必須是絕對的,不能是相對的。
-g:
這是保留調試信息flag。
-g<level>
控制打印的調試信息數量,每一個level都是在前一個level的基礎上編譯的:
note:
優化級別越高,編譯時間越長
--profiling:
--profiling-funcs:
--tracing:
啟用Emscripten的tracing API。
--emit-symbol-map:
--js-opts<level>:
允許JavaScript優化,有兩個值:
0:不允許JavaScript優化器允許;
1:使用JavaScript優化器。
通常用不到我們設置這一項, 因為設置-O后面的level的時候,這個項就能順便取到一個合適的值。
note:
有些選項會重寫這個flag的值,比如EMTERPRETIFY, DEAD_FUNCTIONS, OUTLINING_LIMIT, SAFE_HEAP 和 SPLIT_MEMORY會將js-opts=1,因為他們依賴js優化器。
--llvm-opts<level>:
啟用llvm優化。它的取值有有:
和--js-opts<level>一樣,通常用不到我們設置這一項, 因為設置-O后面的level的時候,這個項就能順便取到一個合適的值。
--llvm-lto<level>:
啟用llvm 連接時 優化??梢匀≈?,1,2,3。
--closure <on>:
運行壓縮編譯器(Closure Compiler),可能的取值有,0,1,2:
--pre-js <file>
生成代碼前,指定一個要把內容添加進來的文件。
--post-js <file>
生成代碼后,指定一個要把內容添加進來的文件。
--embed-file <file>
指定一個帶路徑的文件嵌入到編譯生成的js代碼里。路徑是相對于編譯時的當前路徑。如果傳的是一個目錄,則目錄下所有文件的內容都會被嵌入到將來生成的js代碼中。
--preload-file <name>
異步運行編譯代碼前,指定一個預加載的文件。路徑是相對于編譯時的當前路徑。如果傳的是一個目錄,則目錄下所有文件的內容都會被預加載到一個.data文件中。
--exclude-file <name>
從 –embed-file and –preload-file后面的目錄中排除一些文件,支持使用通配符*。
--use-preload-plugins
告訴文件打包器當文件加載時,運行預加載插件。它用來執行諸如使用瀏覽器解碼器解碼圖片和音頻等。
--shell-file <path>
指定要生成HTML的模板文件。
--source-map-base <base-url>
--minify 0
等于-g1。
--js-transform <cmd>
優化之前,生成代碼之后,設定這一條命令。這條命令可以讓你修改JavaScript代碼。之后,編譯器會將修改的和未修改的一起進行編譯優化。
--bind
啟用bingdings編譯源代碼。bingings是Emscripten中連接C++和JavaScript代碼的一類API。
--ignore-dynamic-linking
告訴編譯器忽視動態鏈接,之后用戶就得手動鏈接到共享庫。
--js-library <lib>
定義除了核心庫(src/library_*)以外的js庫。
-v
打開詳細輸出。
這個設置為把-v傳給clang,并且啟用EMCC_DEBUG生成編譯階段的中間文件。它也會運行Emscripten關于工具鏈的內部的完整性檢查。
tip: emcc -v是診斷錯誤的有用工具,不管你是否附加其他參數。
--cache
--clear-cache
--clear-ports
--show-ports
--save-bc PATH
--memory-init-file <on>
規定是否單獨生成一個內存初始化文件。取值包括0和1.
-Wwarn-absolute-paths
啟用在-I和-L命令行指令中使用絕對路徑的警告。這是用來警告無意中使用了絕對路徑的。在引用非可移植的本地系統頭文件時,使用絕對路徑有時是很危險的。
--proxy-to-worker
--emrun
使生成的代碼能夠感知emrun命令行工具。當運行emran生成的應用程序時,這樣設置就允許stdout、stderr和exit(returncode)被捕獲。
--cpuprofiler
在生成的頁面上嵌入一個簡單的CPU分析器。使用這個來執行粗略的交互式性能分析。
--memoryprofiler
在生成的頁面上嵌入內存分配跟蹤器,使用它來分析應用程序Emscripten堆的使用情況。
--threadprofiler
在生成的頁面上嵌入一個線程活動分析器。當進行多線程編譯時,使用它來分析多線程應用程序。
--em-config
--default-obj-ext .ext
--valid-abspath path
設置一個絕對路徑的白名單,以防止關于絕對路徑的警告。
-o <target>
編譯輸出的文件格式。target可以取值為:
note:
如果你用了--memory-init-file,則還會從js文件中再單獨分出一部分代碼為.mem文件。
-c
生成llvm bitcode代碼,而不是JavaScript。
--separate-asm
把asm.js文件單獨生成到一個文件中。這樣可以減少啟動時的內存加載。
--output_eol windows|linux
規定生成的文本文件的行尾,如果是–output_eol windows,就是windows rn行尾,如果是–output_eol linux,則生成Linux行尾的文本文件。
--cflags
emcc會受到幾個環境變量的影響,如下:
這幾個里面比較有意思的是EMCC_DEBUG。比如,如果你在編譯之前設置set EMCC_DEBUG=1,那么編譯的時候會把編譯過程的調試信息和編譯各個階段的中間文件輸出到一個臨時目錄,這算是給開發者提供一些編譯期間的幫助或者說調試信息吧。
Emscripten主題系列文章是emscripten中文站點的一部分內容。
第一個主題介紹代碼可移植性與限制
第二個主題介紹Emscripten的運行時環境
第三個主題第一篇文章介紹連接C++和JavaScript
第三個主題第二篇文章介紹embind
第四個主題介紹文件和文件系統
第六個主題介紹Emscripten如何調試代碼
這篇文章的主要目的是學會使用koa框架搭建web服務,從而提供一些后端接口,供前端調用。
搭建這個環境的目的是: 前端工程師在跟后臺工程師商定了接口但還未聯調之前,涉及到向后端請求數據的功能能夠走前端工程師自己搭建的http路徑,而不是直接在前端寫幾個死數據。即,模擬后端接口。
當然在這整個過程(搭建環境 + 開發示例demo)中,涉及到以下幾點知識點。
包括:
首先是vue + vue-router + vuex的環境。我們用vue-cli腳手架生成項目,會用vue的同學對這塊應該很熟了。
// 全局安裝腳手架工具 npm i vue-cli -g // 驗證腳手架工具安裝成功與否 vue --version // 構建項目 vue init webpack 項目名 // 測試vue項目是否運行成功 npm run dev
因為腳手架生成的vue項目不包含vuex,所以再安裝vuex。
// 安裝vuex npm i vuex --save
前端項目構建好了,就開始構建我們的后端服務。
首先在你的開發工具(不管是webstorm還是sublime)里新建一個目錄,用來搭建基于koa的web服務。
在這里,我們不妨給這個目錄起名為koa-demo。
然后執行:
// 進入目錄 cd koa-demo // 生成package.json npm init -y // 安裝以下依賴項 npm i koa npm i koa-router npm i koa-cors
安裝好koa和兩個中間件,環境就算搭建完成了。
搭建環境是為了使用,所以我們立馬來寫一個demo出來。
demo開發既是一個練習如何在開發環境中寫代碼的過程,反過來,也是一個驗證環境搭建的對不對、好不好用的過程。
本例中,后端我們只提供一個服務,就是給前端提供一個返回json數據的接口。代碼中包含注釋,所以直接上代碼。
server.js文件
// server.js文件 let Koa = require('koa'); let Router = require('koa-router'); let cors = require('koa-cors'); // 引入modejs的文件系統API let fs = require('fs'); const app = new Koa(); const router = new Router(); // 提供一個/getJson接口 router
.get('/getJson', async ctx => { // 后端允許cors跨域請求 await cors(); // 返回給前端的數據 ctx.body = JSON.parse(fs.readFileSync( './static/material.json'));
}); // 將koa和兩個中間件連起來 app.use(router.routes()).use(router.allowedMethods()); // 監聽3000端口 app.listen(3000);
這里面用到了一個json文件,在'./static/material.json'路徑,該json文件的代碼是:
// material.json文件 [{ "id": 1, "date": "2016-05-02", "name": "張三", "address": "北京 清華大學",
}, { "id": 2, "date": "2016-05-04", "name": "李四", "address": "上海 復旦大學",
}, { "id": 3, "date": "2016-05-01", "name": "王五", "address": "廣東 中山大學",
}, { "id": 4, "date": "2016-05-03", "name": "趙六", "address": "廣東 深圳大學",
}, { "id": 5, "date": "2016-05-05", "name": "韓梅梅", "address": "四川 四川大學",
}, { "id": 6, "date": "2016-05-11", "name": "劉小律", "address": "湖南 中南大學",
}, { "id": 7, "date": "2016-04-13", "name": "曾坦", "address": "江蘇 南京大學",
}]
然后我們是用以下命令將服務啟動
node server.js
打開瀏覽器,輸入http://127.0.0.1:3000/getJson??匆豢错撁嫔鲜欠駥son文件中的json數據顯示出來,如果能夠顯示出來,則說明這個提供json數據的服務,我們已經搭建好了。
為突出重點,排除干擾,方便理解。我們的前端就寫一個組件,組件有兩部分:首先是一個按鈕,用來調用web服務的getJson接口;然后是一個內容展示區域,拿到后端返回的數據以后,將其在組件的這塊區域顯示出來。
首先我們看組件文件吧
<template> <div class="test"> <button type="button" @click="getJson">從后端取json</button> <div class="showJson">{{json}}</div> </div> </template> <script> import {store} from '../vuex' export default { computed: {
json(){ return store.state.json;
}
}, methods: {
getJson(){
store.dispatch("getJson");
}
}
} </script> <style scoped> .showJson{ width:500px; margin:10px auto; min-height:500px; background-color: palegreen;
} </style>
非常簡單,就不多解釋了。
然后看我們的vuex文件。
import Vue from 'vue' import Vuex from 'vuex';
Vue.use(Vuex) const state = { json: [],
}; const mutations = {
setJson(state, db){
state.json = db;
}
} const actions = {
getJson(context){ // 調用我們的后端getJson接口 fetch('http://127.0.0.1:3000/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
},
}).then(function (res) { if(res.status === 200){ return res.json()
}
}).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
})
}
}; export const store = new Vuex.Store({ state: state, mutations: mutations, actions: actions,
})
ok, 代碼擼完了,獲取后端數據之前是這樣的。
獲取后端數據之后是這樣的。
想要把本demo的fetch改為axios方式,要做的工作有以下幾處:
1、安裝axios、在vuex文件引用axios
npm i axios import axios from 'axios'
2、將fetch部分代碼替換為:
const actions = {
getJson(context){
axios.get('/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
},
}).then(function (res) { if(res.status === 200){ return res.data
}
}).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
})
}
};
3、又會遇到跨域,在webpack中修改,路徑config/index.js文件中添加proxyTable項的配置:
proxyTable: { '/json': { target: 'http://127.0.0.1:3000', changeOrigin: true, pathRewrite: { '^/json': '/json' }
}
},
基于vue腳手架搭建的項目,模擬異步取數據,也可以直接在腳手架生成的static文件夾下放置數據,假裝是后臺拿過來的數據。
不過搭建一個基于express或者koa的web服務,確實也該是一個前端工程師應該掌握的。
OK,以上就是全文了。
如果這篇文章使你有所收獲,不勝榮幸。
歡迎點贊,以期能幫助更多同學!
https://github.com/xiangshuo1992/preload.git git@github.com:xiangshuo1992/preload.git
這兩個地址展示的是同一個項目,但是這兩個地址之間有什么聯系呢? 前者是https url 直接有效網址打開,但是用戶每次通過git提交的時候都要輸入用戶名和密碼,有沒有簡單的一點的辦法,一次配置,永久使用呢?當然,所以有了第二種地址,也就是SSH URL,那如何配置就是本文要分享的內容。 GitHub配置SSH Key的目的是為了幫助我們在通過git提交代碼是,不需要繁瑣的驗證過程,簡化操作流程。 步驟一、設置git的user name和email
如果你是第一次使用,或者還沒有配置過的話需要操作一下命令,自行替換相應字段。git config --global user.name "Luke.Deng" git config --global user.email "xiangshuo1992@gmail.com"
二、檢查是否存在SSH Key
cd ~/.ssh ls 或者 ll //看是否存在 id_rsa 和 id_rsa.pub文件,如果存在,說明已經有SSH Key
如果沒有SSH Key,則需要先生成一下
ssh-keygen -t rsa -C "xiangshuo1992@gmail.com"
三、獲取SSH Key
cat id_rsa.pub //拷貝秘鑰 ssh-rsa開頭
四、GitHub添加SSH Key
GitHub點擊用戶頭像,選擇setting
新建一個SSH Key
取個名字,把之前拷貝的秘鑰復制進去,添加就好啦。
五、驗證和修改
測試是否成功配置SSH Key
ssh -T git@github.com //運行結果出現類似如下 Hi xiangshuo1992! You've successfully authenticated, but GitHub does not provide shell access.之前已經是https的鏈接,現在想要用SSH提交怎么辦?
直接修改項目目錄下.git
文件夾下的config
文件,將地址修改一下就好了。
———————————————— 版權聲明:本文為CSDN博主「前端向朔」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/u013778905/java/article/details/83501204
vue中關于插槽的文檔說明很短,語言又寫的很凝練,再加上其和methods,data,computed等常用選項使用頻率、使用先后上的差別,這就有可能造成初次接觸插槽的開發者容易產生“算了吧,回頭再學,反正已經可以寫基礎組件了”,于是就關閉了vue說明文檔。
實際上,插槽的概念很簡單,下面通過分三部分來講。這個部分也是按照vue說明文檔的順序來寫的。
進入三部分之前,先讓還沒接觸過插槽的同學對什么是插槽有一個簡單的概念:插槽,也就是slot,是組件的一塊HTML模板,這塊模板顯示不顯示、以及怎樣顯示由父組件來決定。 實際上,一個slot最核心的兩個問題這里就點出來了,是顯示不顯示和怎樣顯示。
由于插槽是一塊模板,所以,對于任何一個組件,從模板種類的角度來分,其實都可以分為非插槽模板和插槽模板兩大類。
非插槽模板指的是html模板,指的是‘div、span、ul、table’這些,非插槽模板的顯示與隱藏以及怎樣顯示由插件自身控制;插槽模板是slot,它是一個空殼子,因為它顯示與隱藏以及最后用什么樣的html模板顯示由父組件控制。但是插槽顯示的位置確由子組件自身決定,slot寫在組件template的哪塊,父組件傳過來的模板將來就顯示在哪塊。
首先是單個插槽,單個插槽是vue的官方叫法,但是其實也可以叫它默認插槽,或者與具名插槽相對,我們可以叫它匿名插槽。因為它不用設置name屬性。
單個插槽可以放置在組件的任意位置,但是就像它的名字一樣,一個組件中只能有一個該類插槽。相對應的,具名插槽就可以有很多個,只要名字(name屬性)不同就可以了。
下面通過一個例子來展示。
父組件:
-
<template>
-
<div class="father">
-
<h3>這里是父組件</h3>
-
<child>
-
<div class="tmpl">
-
<span>菜單1</span>
-
<span>菜單2</span>
-
<span>菜單3</span>
-
<span>菜單4</span>
-
<span>菜單5</span>
-
<span>菜單6</span>
-
</div>
-
</child>
-
</div>
-
</template>
子組件:
-
<template>
-
<div class="child">
-
<h3>這里是子組件</h3>
-
<slot></slot>
-
</div>
-
</template>
在這個例子里,因為父組件在<child></child>里面寫了html模板,那么子組件的匿名插槽這塊模板就是下面這樣。也就是說,子組件的匿名插槽被使用了,是被下面這塊模板使用了。
-
<div class="tmpl">
-
<span>菜單1</span>
-
<span>菜單2</span>
-
<span>菜單3</span>
-
<span>菜單4</span>
-
<span>菜單5</span>
-
<span>菜單6</span>
-
</div>
最終的渲染結果如圖所示:
-
-
注:所有demo都加了樣式,以方便觀察。其中,父組件以灰色背景填充,子組件都以淺藍色填充。
匿名插槽沒有name屬性,所以是匿名插槽,那么,插槽加了name屬性,就變成了具名插槽。具名插槽可以在一個組件中出現N次。出現在不同的位置。下面的例子,就是一個有兩個具名插槽和單個插槽的組件,這三個插槽被父組件用同一套css樣式顯示了出來,不同的是內容上略有區別。
父組件:
-
<template>
-
<div class="father">
-
<h3>這里是父組件</h3>
-
<child>
-
<div class="tmpl" slot="up">
-
<span>菜單1</span>
-
<span>菜單2</span>
-
<span>菜單3</span>
-
<span>菜單4</span>
-
<span>菜單5</span>
-
<span>菜單6</span>
-
</div>
-
<div class="tmpl" slot="down">
-
<span>菜單-1</span>
-
<span>菜單-2</span>
-
<span>菜單-3</span>
-
<span>菜單-4</span>
-
<span>菜單-5</span>
-
<span>菜單-6</span>
-
</div>
-
<div class="tmpl">
-
<span>菜單->1</span>
-
<span>菜單->2</span>
-
<span>菜單->3</span>
-
<span>菜單->4</span>
-
<span>菜單->5</span>
-
<span>菜單->6</span>
-
</div>
-
</child>
-
</div>
-
</template>
子組件:
-
<template>
-
<div class="child">
-
// 具名插槽
-
<slot name="up"></slot>
-
<h3>這里是子組件</h3>
-
// 具名插槽
-
<slot name="down"></slot>
-
// 匿名插槽
-
<slot></slot>
-
</div>
-
</template>
顯示結果如圖:
可以看到,父組件通過html模板上的slot屬性關聯具名插槽。沒有slot屬性的html模板默認關聯匿名插槽。
最后,就是我們的作用域插槽。這個稍微難理解一點。官方叫它作用域插槽,實際上,對比前面兩種插槽,我們可以叫它帶數據的插槽。什么意思呢,就是前面兩種,都是在組件的template里面寫
-
匿名插槽
-
<slot></slot>
-
具名插槽
-
<slot name="up"></slot>
但是作用域插槽要求,在slot上面綁定數據。也就是你得寫成大概下面這個樣子。
-
<slot name="up" :data="data"></slot>
-
export default {
-
data: function(){
-
return {
-
data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
-
}
-
},
-
}
我們前面說了,插槽最后顯示不顯示是看父組件有沒有在child下面寫模板,像下面那樣。
-
<child>
-
html模板
-
</child>
寫了,插槽就總得在瀏覽器上顯示點東西,東西就是html該有的模樣,沒寫,插槽就是空殼子,啥都沒有。
OK,我們說有html模板的情況,就是父組件會往子組件插模板的情況,那到底插一套什么樣的樣式呢,這由父組件的html+css共同決定,但是這套樣式里面的內容呢?
正因為作用域插槽綁定了一套數據,父組件可以拿來用。于是,情況就變成了這樣:樣式父組件說了算,但內容可以顯示子組件插槽綁定的。
我們再來對比,作用域插槽和單個插槽和具名插槽的區別,因為單個插槽和具名插槽不綁定數據,所以父組件是提供的模板要既包括樣式由包括內容的,上面的例子中,你看到的文字,“菜單1”,“菜單2”都是父組件自己提供的內容;而作用域插槽,父組件只需要提供一套樣式(在確實用作用域插槽綁定的數據的前提下)。
下面的例子,你就能看到,父組件提供了三種樣式(分別是flex、ul、直接顯示),都沒有提供數據,數據使用的都是子組件插槽自己綁定的那個人名數組。
父組件:
-
<template>
-
<div class="father">
-
<h3>這里是父組件</h3>
-
<!--第一次使用:用flex展示數據-->
-
<child>
-
<template slot-scope="user">
-
<div class="tmpl">
-
<span v-for="item in user.data">{{item}}</span>
-
</div>
-
</template>
-
-
</child>
-
-
<!--第二次使用:用列表展示數據-->
-
<child>
-
<template slot-scope="user">
-
<ul>
-
<li v-for="item in user.data">{{item}}</li>
-
</ul>
-
</template>
-
-
</child>
-
-
<!--第三次使用:直接顯示數據-->
-
<child>
-
<template slot-scope="user">
-
{{user.data}}
-
</template>
-
-
</child>
-
-
<!--第四次使用:不使用其提供的數據, 作用域插槽退變成匿名插槽-->
-
<child>
-
我就是模板
-
</child>
-
</div>
-
</template>
子組件:
-
<template>
-
<div class="child">
-
-
<h3>這里是子組件</h3>
-
// 作用域插槽
-
<slot :data="data"></slot>
-
</div>
-
</template>
-
-
export default {
-
data: function(){
-
return {
-
data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
-
}
-
}
-
}
結果如圖所示:
以上三個demo就放在GitHub了,有需要的可以去取。使用非常方便,是基于vue-cli搭建工程。
藍藍設計的小編 http://www.syprn.cn