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
1.1需求描述
采用vue.js開發搜索界面則SEO不友好,需要解決SEO的問題。
1.2了解SEO
總結:seo是網站為了提高自已的網站排名,獲得更多的流量,對網站的結構及內容進行調整優化,以便搜索引擎
(百度,google等)更好抓取到更優質的網站的內容。
下圖是搜索引擎爬取網站頁面的大概流程圖:
(搜索引擎的工作流程很復雜,下圖只是簡單概括)
從上圖可以看到SEO是網站自己為了方便spider抓取網頁而作出的網頁內容優化,常見的SEO方法比如:
1)對url鏈接的規范化,多用restful風格的url,多用靜態資源url;
2) 注意title、keywords的設置。
3)由于spider對javascript支持不好,對于網頁跳轉用href標簽。
。。。
1.3服務端渲染和客戶端渲染
采用什么技術有利于SEO?要解答這個問題需要理解服務端渲染和客戶端渲染。什么是服務端渲染?
我們用傳統的servlet開發來舉例:瀏覽器請求servlet,servlet在服務端生成html響應給瀏覽器,瀏覽器展示html 的內容,這個過程就是服務端渲染,如下圖:
服務端渲染的特點:
1)在服務端生成html網頁的dom元素。
2)客戶端(瀏覽器)只負責顯示dom元素內容。
當初隨著web2.0的到來,A JAX技術興起,出現了客戶端渲染:客戶端(瀏覽器) 使用A JAX向服務端發起http請求,獲取到了想要的數據,客戶端拿著數據開始渲染html網頁,生成Dom元素,并最終將網頁內容展示給用戶, 如下圖:
S
客戶端渲染的特點:
1)在服務端只是給客戶端響應的了數據,而不是html網頁
2)客戶端(瀏覽器)負責獲取服務端的數據生成Dom元素。
兩種方式各有什么優缺點? 客戶端渲染:
1)缺點
不利于網站進行SEO,因為網站大量使用javascript技術,不利于spider抓取網頁。
2)優點
客戶端負責渲染,用戶體驗性好,服務端只提供數據不用關心用戶界面的內容,有利于提高服務端的開發效率。
3)適用場景
對SEO沒有要求的系統,比如后臺管理類的系統,如電商后臺管理,用戶管理等。
服務端渲染:
1)優點
有利于SEO,網站通過href的url將spider直接引到服務端,服務端提供優質的網頁內容給spider。
2)缺點
服務端完成一部分客戶端的工作,通常完成一個需求需要修改客戶端和服務端的代碼,開發效率低,不利于系統的 穩定性。
3)適用場景
對SEO有要求的系統,比如:門戶首頁、商品詳情頁面等。
2.1Nuxt.js介紹
移動互聯網的興起促進了web前后端分離開發模式的發展,服務端只專注業務,前端只專注用戶體驗,前端大量運 用的前端渲染技術,比如流行的vue.js、react框架都實現了功能強大的前端渲染。
但是,對于有SEO需求的網頁如果使用前端渲染技術去開發就不利于SEO了,有沒有一種即使用vue.js、react的前 端技術也實現服務端渲染的技術呢?其實,對于服務端渲染的需求,vue.js、react這樣流行的前端框架提供了服務端渲染的解決方案。
從上圖可以看到:
react框架提供next.js實現服務端渲染。
vue.js框架提供Nuxt.js實現服務端渲染。
2.2Nuxt.js工作原理
下圖展示了從客戶端請求到Nuxt.js進行服務端渲染的整體的工作流程:
1、用戶打開瀏覽器,輸入網址請求到Node.js
2、部署在Node.js的應用Nuxt.js接收瀏覽器請求,并請求服務端獲取數據
3、Nuxt.js獲取到數據后進行服務端渲染 4、Nuxt.js將html網頁響應給瀏覽器
Nuxt.js使用了哪些技術?
Nuxt.js使用Vue.js+webpack+Babel三大技術框架/組件,如下圖:
Babel 是一個js的轉碼器,負責將ES6的代碼轉成瀏覽器識別的ES5代碼。
Webpack是一個前端工程打包工具。Vue.js是一個優秀的前端框架。
Nuxt.js的特性有哪些?
基 于 Vue.js
自動代碼分層服務端渲染
強大的路由功能,支持異步數據靜態文件服務
ES6/ES7 語法支持
打包和壓縮 JS 和 CSS HTML頭部標簽管理本地開發支持熱加載集成ESLint
支持各種樣式預處理器: SASS、LESS、 Stylus等等
3.1創建Nuxt工程
nuxt.js有標準的目錄結構,官方提供了模板工程,可以模板工程快速創建nuxt項目。
模板工程地址:https://github.com/nuxt-community/starter-template/archive/master.zip
本項目提供基于Nuxt.js的封裝工程,基于此封裝工程開發搜索前端,見“資料”–》xc-ui-pc-portal.zip,解壓
xc-ui-pc-portal.zip到本項目前端工程目錄下。
本前端工程屬于門戶的一部分,將承載一部分考慮SEO的非靜態化頁面。
本工程基于Nuxt.js模板工程構建,Nuxt.js使用1.3版本,并加入了今后開發中所使用的依賴包,直接解壓本工程即 可使用。
3.2目錄結構
本工程的目錄結構如下:
‐資源目錄 資源目錄 assets 用于組織未編譯的靜態資源如 LESS、SASS 或 JavaScript。 ‐組件目錄 組件目錄 components 用于組織應用的 Vue.js 組件。Nuxt.js 不會擴展增強該目錄下 Vue.js 組件,即這些組件不會像頁面組件那樣有 asyncData 方法的特性。 ‐布局目錄 布局目錄 layouts 用于組織應用的布局組件。該目錄名為Nuxt.js保留的,不可更改。 ‐中間件目錄 middleware 目錄用于存放應用的中間件。 ‐頁面目錄 頁面目錄 pages 用于組織應用的路由及視圖。Nuxt.js 框架讀取該目錄下所有的 .vue 文件并自動生成對應的路由配置。 該目錄名為Nuxt.js保留的,不可更改。 ‐插件目錄 插件目錄 plugins 用于組織那些需要在 根vue.js應用 實例化之前需要運行的 Javascript 插件。 ‐靜態文件目錄 靜態文件目錄 static 用于存放應用的靜態文件,此類文件不會被 Nuxt.js 調用 Webpack 進行構建編譯處理。 服務器啟動的時候,該目錄下的文件會映射至應用的根路徑 / 下。 舉個例子: /static/logo.png 映射至 /logo.png 該目錄名為Nuxt.js保留的,不可更改。 ‐Store 目 錄 store 目錄用于組織應用的 Vuex 狀態樹 文件。 Nuxt.js 框架集成了 Vuex 狀態樹 的相關功能配置,在 store 目錄下創建一個 index.js 文件可激活這些配置。 該目錄名為Nuxt.js保留的,不可更改。 ‐nuxt.config.js 文 件 nuxt.config.js 文件用于組織Nuxt.js 應用的個性化配置,以便覆蓋默認配置。該文件名為Nuxt.js保留的,不可更改。 ‐package.json 文 件 package.json 文件用于描述應用的依賴關系和對外暴露的腳本接口。 該文件名為Nuxt.js保留的,不可更改。
nuxt.js 提供了目錄的別名,方便在程序中引用:
3.3頁面布局
頁面布局就是頁面內容的整體結構,通過在layouts目錄下添加布局文件來實現。在layouts 根目錄下的所有文件都屬于個性化布局文件,可以在頁面組件中利用 layout 屬性來引用。
一個例子:
1、定義:layouts/test.vue布局文件,如下:
注意:布局文件中一定要加 <nuxt/> 組件用于顯示頁面內容。
<template>
<div>
<div>這里是頭</div>
<nuxt/>
<div>這里是尾</div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
2、在pages目錄創建user目錄,并創建index.vue頁面
在 pages/user/index.vue 頁面里, 可以指定頁面組件使用 test 布局,代碼如下:
<template>
<div>
測試頁面
</div>
</template>
<script>
export default{ layout:'test'
}
</script>
<style>
</style>
3、測試,請求:http://localhost:10000/user,如果如下:
這里是頭測試頁面這里是尾
3.4路由
3.4.1基礎路由
Nuxt.js 依據目錄結構自動生成 vue-router 模塊的路由配置。
Nuxt.js根據pages的目錄結構及頁面名稱定義規范來生成路由,下邊是一個基礎路由的例子:
假設的目錄結構如下:
pages/ ‐‐| user/ ‐‐‐‐‐| index.vue ‐‐‐‐‐| one.vue
那么,Nuxt.js 自動生成的路由配置如下:
router: { routes: [ { name: 'user', path: '/user', component: 'pages/user/index.vue' }, { name: 'user‐one', path: '/user/one', component: 'pages/user/one.vue' } ] }
index.vue代碼如下:
<template> <div> 用戶管理首頁 </div> </template> <script> export default{ layout:"test" } </script> <style> </style>
one.vue代碼如下:
<template> <div> one頁面 </div> </template> <script> export default{ layout:"test" } </script> <style> </style>
分別訪問如下鏈接進行測試:
http://localhost:10000/user http://localhost:10000/user/one
3.4.2嵌套路由
你可以通過 vue-router 的子路由創建 Nuxt.js 應用的嵌套路由。
創建內嵌子路由,你需要添加一個 Vue 文件,同時添加一個與該文件同名的目錄用來存放子視圖組件。
別忘了在父級 Vue 文件內增加假設文件結構如:
pages/ ‐‐| user/ ‐‐‐‐‐| _id.vue ‐‐‐‐‐| index.vue ‐‐| user.vue
Nuxt.js 自動生成的路由配置如下:
router: { routes: [ { path: '/user', component: 'pages/user.vue', children: [ { path: '', component: 'pages/user/index.vue', name: 'user' }, { path: ':id', component: 'pages/user/_id.vue', name: 'user‐id' } ] } ] }
將user.vue文件創建到與user目錄的父目錄下,即和user目錄保持平級。
<template> <div> 用戶管理導航,<nuxt‐link :to="'/user/101'">修改</nuxt‐link> <nuxt‐child/> </div> </template> <script> export default{ layout:"test" } </script> <style> </style>
_id.vue頁面實現了向頁面傳入id參數,頁面內容如下:
<template> <div> 修改用戶信息{{id}} </div> </template> <script> export default{ layout:"test", data(){ return { id:'' } }, mounted(){ this.id=this.$route.params.id; console.log(this.id) } } </script> <style> </style>
測試:http://localhost:10000/user
點擊修改:
3.6獲取數據
3.6.1asyncData 方法
Nuxt.js 擴展了 Vue.js,增加了一個叫 asyncData 的方法,
方法會在組件(限于頁面組件)每次加載之前被調用。它可以在服務端或路由更新之前被調用。 在這個方法被調用的時候,第一個參數被設定為當前頁面的上下文對象,你可以利用返回的數據一并返回給當前組件。方法來獲取數據,Nuxt.js 會將返回的數據融合組件方法
注意:由于對象。
例子:
方法是在組件 初始化 前被調用的,所以在方法內是沒有辦法通過來引用組件的實例
在上邊例子中的user/_id.vue中添加,頁面代碼如下:
<template> <div> 修改用戶信息{{id}},名稱:{{name}} </div> </template> <script> export default{ layout:'test', //根據id查詢用戶信息 asyncData(){ console.log("async方法") return { name:'黑馬程序員' } }, data(){ return { id:'' } }, mounted(){ this.id=this.$route.params.id; } } </script> <style> </style>
此方法在服務端被執行,觀察服務端控制臺打印輸出“async方法”。
此方法返回data模型數據,在服務端被渲染,最后響應給前端,刷新此頁面查看頁面源代碼可以看到name模型數據已在頁面源代碼中顯示。
3.6.2async /await方法
使用async 和 await配合promise也可以實現同步調用,nuxt.js中使用async/await實現同步調用效果。1、先測試異步調用,增加a、b兩個方法,并在mounted中調用。
methods:{ a(){ return new Promise(function(resolve,reject){ setTimeout(function () { resolve(1) },2000) }) }, b(){ return new Promise(function(resolve,reject){ setTimeout(function () { resolve(2) },1000) }) } }, mounted(){ this.a().then(res=>{ alert(res) console.log(res) }) this.b().then(res=>{ alert(res) console.log(res) }) }
2、使用async/await完成同步調用
async asyncData({ store, route }) { console.log("async方法") var a=await new Promise(function (resolve, reject) { setTimeout(function () { console.log("1") resolve(1) },2000) }); var a=await new Promise(function (resolve, reject) { setTimeout(function () { console.log("2") resolve(2) },1000) }); return { name:'黑馬程序員' } },
觀察服務端控制臺發現是按照a、b方法的調用順序輸出1、2,實現了使用async/await完成同步調用。
3.1搜索頁面
3.1.1需求分析
觀察服務端控制臺發現是按照a、b方法的調用順序輸出1、2,實現了使用async/await完成同步調用。
3.1搜索頁面
3.1.1需求分析
<template> <div> <Header /> <nuxt/> <Footer /> </div> </template> <script> import Footer from '../components/Footer.vue' import Header from '../components/Header.vue' export default { components: { Header, Footer } } </script> <style> </style>
3.1.3Nginx代理配置
搜索頁面中以/static開頭的靜態資源通過nginx解析,如下:
/static/plugins:指向門戶目錄下的plugins目錄。
/static/css:指向門戶目錄下的的css目錄
修改Nginx中www.xuecheng.com虛擬主機的配置:
#靜態資源,包括系統所需要的圖片,js、css等靜態資源location /static/img/ { alias F:/develop/xc_portal_static/img/; } location /static/css/ { alias F:/develop/xc_portal_static/css/; } location /static/js/ { alias F:/develop/xc_portal_static/js/; } location /static/plugins/ { alias F:/develop/xc_portal_static/plugins/; add_header Access‐Control‐Allow‐Origin http://ucenter.xuecheng.com; add_header Access‐Control‐Allow‐Credentials true; add_header Access‐Control‐Allow‐Methods GET; }
配置搜索Url,下圖是Nginx搜索轉發流程圖:
用戶請求/course/search時Nginx將請求轉發到nuxt.js服務,nginx在轉發時根據每臺nuxt服務的負載情況進行轉 發,實現負載均衡。
本教程開發環境Nuxt.js服務和www.xuecheng.com虛擬機主在同一臺計算機,使用同一個nginx,配置如下:
#前端門戶課程搜索 location ^~ /course/search { proxy_pass http://dynamic_portal_server_pool; } #后端搜索服務 location /openapi/search/ { proxy_pass http://search_server_pool/search/; } #分類信息 location /static/category/ { proxy_pass http://static_server_pool; } #前端動態門戶 upstream dynamic_portal_server_pool{ server 127.0.0.1:10000 weight=10; } #后臺搜索(公開api) upstream search_server_pool{ server 127.0.0.1:40100 weight=10; }
其它配置:
#開發環境webpack定時加載此文件location ^~ / webpack_hmr { proxy_pass http://dynamic_portal_server_pool/ webpack_hmr; } #開發環境nuxt訪問_nuxt location ^~ /_nuxt/ { proxy_pass http://dynamic_portal_server_pool/_nuxt/; }
在靜態虛擬主機中添加:
#學成網靜態資源server { listen 91; server_name localhost; #分類信息 location /static/category/ { alias F:/develop/xuecheng/static/category/; } ...
3.1.4搜索頁面
創建搜索頁面如下:
3.1.4搜索頁面
創建搜索頁面如下:
//配置文件
let config=require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {
head() { return {
title: '傳智播客‐一樣的教育,不一樣的品質',
meta: [
{charset: 'utf‐8'},
{name: 'description', content: '傳智播客專注IT培訓,Java培訓,Android培訓,安卓培訓,PHP培
訓,C++培訓,網頁設計培訓,平面設計培訓,UI設計培訓,移動開發培訓,網絡營銷培訓,web前端培訓,云計算大數據培訓, 全棧工程師培訓,產品經理培訓。'},
{name: 'keywords', content: this.keywords}
],
link: [
{rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},
{rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},
{rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}
]
}
},
<script>
//配置文件
let config=require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {
head() { return {
title: '傳智播客‐一樣的教育,不一樣的品質',
meta: [
{charset: 'utf‐8'},
{name: 'description', content: '傳智播客專注IT培訓,Java培訓,Android培訓,安卓培訓,PHP培 訓,C++培訓,網頁設計培訓,平面設計培訓,UI設計培訓,移動開發培訓,網絡營銷培訓,web前端培訓,云計算大數據培訓, 全棧工程師培訓,產品經理培訓。'},
{name: 'keywords', content: this.keywords}
],
link: [
{rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},
{rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},
{rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}
]
}
},
async asyncData({ store, route }) { return {
courselist: {},
first_category:{},
second_category:{}, mt:'',
st:'',
grade:'',
keyword:'', total:0,
imgUrl:config.imgUrl
}
},
data() { return {
courselist: {}, first_category:{}, second_category:{}, mt:'',
st:'',
grade:'',
keyword:'', imgUrl:config.imgUrl, total:0,//總記錄數page:1,//頁碼page_size:12//每頁顯示個數
}
},
watch:{//路由發生變化立即搜索search表示search方法'$route':'search'
},
methods: {
//分頁觸發handleCurrentChange(page) {
},
//搜索方法search(){
//刷新當前頁面
window.location.reload();
}
}
}
</script>
3.1.5測試
重啟Nginx,請求:http://www.xuecheng.com/course/search,頁面效果如下:
3.2查詢全部
3.2.1需求分析
初次進入頁面,沒有輸入任何查詢條件,默認查詢全部課程,分頁顯示。
3.2.2API方法
在api目錄創建本工程所用的api方法類,api方法類使用了public.js等一些抽取類:
/api/public.js抽取axios 的基礎方法
/api/util.js工具類
/con?g/sysCon?g.js系統配置類,配置了系統參數變量
創建course.js,作為課程相關業務模塊的api方法類。
import http from './public' import qs from 'qs' let config=require('~/config/sysConfig') let apiURL=config.apiURL let staticURL=config.staticURL if (typeof window==='undefined') { apiURL=config.backApiURL staticURL=config.backStaticURL } /*搜索*/ export const search_course=(page,size,params)=> { let querys=qs.stringify(params); return http.requestQuickGet(apiURL+"/search/course/list/"+page+"/"+size+"?"+querys); }
3.2.3搜索方法
實現思路如下:
1、用戶請求本頁面到達node.js
2、在asyncData方法中向服務端請求查詢課程 3、asyncData方法執行完成開始服務端渲染
在asyncData中執行搜索,代碼如下:
async asyncData({ store, route }) {//服務端調用方法 //搜索課程 let page=route.query.page; if(!page){ page=1; }else{ page=Number.parseInt(page) } console.log(page); //請求搜索服務,搜索服務 let course_data=await courseApi.search_course(page,2,route.query); console.log(course_data) if (course_data && course_data.queryResult ) { let keywords='' let mt='' let st='' let grade='' let keyword='' let total=course_data.queryResult.total if( route.query.mt){ mt=route.query.mt } if( route.query.st){ st=route.query.st } if( route.query.grade){ grade=route.query.grade } if( route.query.keyword){ keyword=route.query.keyword } return { courselist: course_data.queryResult.list,//課程列表keywords:keywords, mt:mt, st:st, grade:grade, keyword:keyword, page:page, total:total, imgUrl:config.imgUrl } }else{ return { courselist: {}, first_category:{}, second_category:{}, mt:'', st:'', grade:'', keyword:'', page:page, total:0, imgUrl:config.imgUrl } } }
3.2.5 頁面
在頁面中展示課程列表。
<div class="content‐list"> <div class="recom‐item" v‐for="(course, index) in courselist"> <nuxt‐link :to="'/course/detail/'+course.id+'.html'" target="_blank"> <div v‐if="course.pic"> <p><img :src="imgUrl+'/'+course.pic" width="100%" alt=""></p> </div> <div v‐else> <p><img src="/img/widget‐demo1.png" width="100%" alt=""></p> </div> <ul > <li class="course_title"><span v‐html="course.name"></span></li> <li style="float: left"><span v‐if="course.charge=='203001'">免費</span> <span v‐if="course.charge=='203002'">¥{{course.price | money}}</span> <!‐‐ <em> ? </em>‐‐> <!‐‐<em>1125人在學習</em>‐‐></li> </ul> </nuxt‐link> </div> <li class="clearfix"></li> </div>
3.3分頁查詢
3.3.1服務端代碼
... //分頁 //當前頁碼 if(page<=0){ page=1; } //起始記錄下標 int from=(page ‐1) * size; searchSourceBuilder.from(from); searchSourceBuilder.size(size); ...
3.3.2前端代碼
使用Element-UI的el-pagination分頁插件:
<div style="text‐align: center"> <el‐pagination background layout="prev, pager, next" @current‐change="handleCurrentChange" :total="total" :page‐size="page_size" :current‐page="page" prev‐text="上一頁" next‐text="下一頁"> </el‐pagination> </div>
定義分頁觸發方法:
methods:{ //分頁觸發handleCurrentChange(page) { this.page=page this.$route.query.page=page let querys=querystring.stringify(this.$route.query) window.location='/course/search?'+querys; } ...
3.4按分類搜索
3.4.1需求分析
1、通過一級分類搜索
2、選擇一級分類后將顯示下屬的二級分類
3、選擇二分類進行搜索
4、選擇一級分類的全部則表示沒有按照分類搜索
5、選擇一級分類的全部時二級分類不顯示
3.4.2API方法
課程分類將通過頁面靜態化的方式寫入靜態資源下,通過/category/category.json可訪問,通過
www.xuecheng.com/static/category/category.json即可訪問。
category.json的內容如下:
我們需要定義api方法獲取所有的分類在/api/course.js中添加:
/*獲取分類*/ export const sysres_category=()=> { return http.requestQuickGet(staticURL+"/static/category/category.json"); }
3.4.3在asyncData中查詢分類
進入搜索頁面將默認顯示所有一級分類,當前如果已選擇一級分類則要顯示所有一級分類及該一級分類下屬的二級 分類。
在asyncData方法中實現上邊的需求,代碼如下:
async asyncData({ store, route }) {//服務端調用方法 //搜索課程 let page=route.query.page; if(!page){ page=1; }else{ page=Number.parseInt(page) } console.log(page); //請求搜索服務,搜索服務 let course_data=await courseApi.search_course(page,2,route.query); console.log(course_data) //查詢分類 let category_data=await courseApi.sysres_category() if (course_data && course_data.queryResult ) { //全部分類 let category=category_data.category//分部分類 let first_category=category[0].children//一級分類let second_category=[]//二級分類 let keywords='' let mt='' let st='' let grade='' let keyword='' let total=course_data.queryResult.total if( route.query.mt){ mt=route.query.mt } if( route.query.st){ st=route.query.st } if( route.query.grade){ grade=route.query.grade } if( route.query.keyword){ keyword=route.query.keyword } //遍歷一級分類 for(var i in first_category){ keywords+=first_category[i].name+' ' if(mt!=''&& mt==first_category[i].id){ //取出二級分類 second_category=first_category[i].children; // console.log(second_category) break; } } return { courselist: course_data.queryResult.list,//課程列表first_category: first_category, second_category: second_category, keywords:keywords, mt:mt, st:st, grade:grade, keyword:keyword, page:page, total:total, imgUrl:config.imgUrl } }else{ return { courselist: {}, first_category:{}, second_category:{}, mt:'', st:'', grade:'', keyword:'', page:page, total:0, imgUrl:config.imgUrl } } }
3.3.4頁面
在頁面顯示一級分類及二級分類,需要根據當前是否選擇一級分類、是否選擇二分類顯示頁面內容。
<ul> <li>一級分類:</li> <li v‐if="mt!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&grade='+grade">全部</nuxt‐link></li> <li class="all" v‐else>全部</li> <ol> <li v‐for="category_v in first_category"> <nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐if="category_v.id==mt">{{category_v.name}}</nuxt‐link> <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link> </li> </ol> <!‐‐<ol> <li>數據分析</li> <li>機器學習工程</li> <li>前端開發工程</li> </ol>‐‐> </ul> <ul> <li>二級分類:</li> <li v‐if="st!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&mt='+mt+'&grade='+grade">全部</nuxt‐link></li> <li class="all" v‐else>全部</li> <ol v‐if="second_category.length>0"> <li v‐for="category_v in second_category"> <nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st=' + category_v.id" v‐if="category_v.id==st">{{category_v.name}}</nuxt‐link> <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link> </li> <!‐‐ <li>大數據</li> <li>云計算</li>‐‐> </ol> <!‐‐<a href="#" class="more">更多 ∨</a>‐‐> </ul>
當用戶點擊分類時立即執行搜索,實現思路如下:
1)點擊分類立即更改路由。
2)通過監聽路由,路由更改則刷新頁面。
1)創建搜索方法
search(){ //刷新當前頁面window.location.reload(); }
2)定義watch
通過vue.js的watch可以實現監視某個變量,當變量值出現變化時執行某個方法。 實現思路是:
1、點擊分類頁面路由更改
2、通過watch監視路由,路由更改觸發search方法與methods并行定義watch:
watch:{//路由發生變化立即搜索search表示search方法'$route':'search' },
3.5按難度等級搜索
3.5.1需求分析
用戶選擇不同的課程難度等級去搜索課程。
3.5.2API方法
使用 search_course方法完成搜索。
3.5.3頁面
按難度等級搜索思路如下:
1)點擊難度等級立即更改路由。
2)通過監聽路由,路由更改則立即執行search搜索方法。按難度等級搜索頁面代碼如下:
<ul> <li>難度等級:</li> <li v‐if="grade!=''"> <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade='">全部 </nuxt‐link> </li> <li class="all" v‐else>全部</li> <ol> <li v‐if="grade=='200001'" class="all">初級</li> <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200001'">初級</nuxt‐link></li> <li v‐if="grade=='200002'" class="all">中級</li> <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200002'">中級</nuxt‐link></li> <li v‐if="grade=='200003'" class="all">高級</li> <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200003'">高級</nuxt‐link></li> </ol> </ul>
3.6高亮顯示
3.6.1服務端代碼
修改service的搜索方法,添加高亮設置:
... //定義高亮 HighlightBuilder highlightBuilder=new HighlightBuilder(); highlightBuilder.preTags("<font class='eslight'>"); highlightBuilder.postTags("</font>"); highlightBuilder.fields().add(new HighlightBuilder.Field("name")); searchSourceBuilder.highlighter(highlightBuilder); ... //解析高亮字段 for(SearchHit hit:searchHits){ CoursePub coursePub=new CoursePub(); //源文檔 Map<String, Object> sourceAsMap=hit.getSourceAsMap(); //課程id String id=(String) sourceAsMap.get("id"); coursePub.setId(id); //取出name String name=(String) sourceAsMap.get("name"); //取出高亮字段 Map<String, HighlightField> highlightFields=hit.getHighlightFields(); if(highlightFields.get("name")!=null){ HighlightField highlightField=highlightFields.get("name"); Text[] fragments=highlightField.fragments(); StringBuffer stringBuffer=new StringBuffer(); for(Text text:fragments){ stringBuffer.append(text); } name=stringBuffer.toString(); } coursePub.setName(name); ....
3.6.2前端代碼
在search/index.vue中定義eslight樣式:
<style> .eslight{ color: red; } ...
4.1需求分析
本次集成測試的目的如下:
1、測試課程發布與CMS接口是否正常。
2、測試課程發布與ES接口是否正常。
3、測試課程從創建到發布的整個過程。
4.2準備環境
1、啟動MySQL、MongoDB
2、啟動ElasticSearch、RabbitMQ 3、啟動Eureka Server
4、啟動CMS、課程管理服務、搜索服務。
5、啟動Nginx、系統管理前端、教學管理前端、Nuxt.js。
試中常被問到的問題,此問題包含web開發中從前端到后端到運維的絕大多數知識,主要考察面試者知識的廣度。本文會根據作者了解的程度增加不斷更新,不足之處歡迎評論區補充。
首先了解一下URL的組成:
http://www.baidu.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
從上面的URL可以看出,一個完整的URL包括以下幾部分:
1、協議部分:該URL的協議部分為“http:”,這代表網頁使用的是HTTP協議。在Internet中可以使用多種協議,如HTTP,FTP等等本例中使用的是HTTP協議。在"HTTP"后面的“//”為分隔符
2、域名部分:該URL的域名部分為“www.baidu.com”。一個URL中,也可以使用IP地址作為域名使用
3、端口部分:跟在域名后面的是端口,域名和端口之間使用“:”作為分隔符。端口不是一個URL必須的部分,如果省略端口部分,將采用默認端口80
4、虛擬目錄部分:從域名后的第一個“/”開始到最后一個“/”為止,是虛擬目錄部分。虛擬目錄也不是一個URL必須的部分。本例中的虛擬目錄是“/news/”
5、文件名部分:從域名后的最后一個“/”開始到“?”為止,是文件名部分,如果沒有“?”,則是從域名后的最后一個“/”開始到“#”為止,是文件部分,如果沒有“?”和“#”,那么從域名后的最后一個“/”開始到結束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一個URL必須的部分,如果省略該部分,則使用默認的文件名
6、錨部分:從“#”開始到最后,都是錨部分。本例中的錨部分是“name”。錨部分也不是一個URL必須的部分
7、參數部分:從“?”開始到“#”為止之間的部分為參數部分,又稱搜索部分、查詢部分。本例中的參數部分為“boardID=5&ID=24618&page=1”。參數可以允許有多個參數,參數與參數之間用“&”作為分隔符。
很多大公司面試喜歡問這樣一道面試題,輸入URL到看見頁面發生了什么?,今天我們來總結一下。 簡單來說,共有以下幾個過程
下面我們來看看具體的細節
發送至 DNS 服務器并獲得域名對應的 WEB 服務器的 ip 地址。
DNS 解析首先會從你的瀏覽器的緩存中去尋找是否有這個網址對應的 IP 地址,如果沒有就向OS系統的 DNS 緩存中尋找,如果沒有就是路由器的 DNS 緩存, 如果沒有就是 ISP 的DNS 緩存中尋找。 所以,緩存的尋找過程就是: 瀏覽器 -> 系統 -> 路由器 -> ISP。 如果在某一個緩存中找到的話,就直接跳到下一步。 如果都沒有找到的話,就會向 ISP 或者公共的域名解析服務發起 DNS 查找請求。這個查找的過程還是一個遞歸查詢的過程。
輸入www.google.com網址后,首先在本地的域名服務器中查找,沒找到去根域名服務器查找,沒有再去com頂級域名服務器查找,,如此的類推下去,直到找到IP地址,然后把它記錄在本地,供下次使用。大致過程就是. -> .com -> google.com. -> www.google.com.。 (你可能覺得我多寫 .,并木有,這個.對應的就是根域名服務器,默認情況下所有的網址的最后一位都是.,既然是默認情況下,為了方便用戶,通常都會省略,瀏覽器在請求DNS的時候會自動加上)
既然已經懂得了解析的具體過程,我們可以看到上述一共經過了N個過程,每個過程有一定的消耗和時間的等待,因此我們得想辦法解決一下這個問題!
DNS存在著多級緩存,從離瀏覽器的距離排序的話,有以下幾種: 瀏覽器緩存,系統緩存,路由器緩存,IPS服務器緩存,根域名服務器緩存,頂級域名服務器緩存,主域名服務器緩存。
在你的chrome瀏覽器中輸入:chrome://dns/,你可以看到chrome瀏覽器的DNS緩存。
系統緩存主要存在/etc/hosts(Linux系統)中
檢查瀏覽器是否有緩存
通過Cache-Control和Expires來檢查是否命中強緩存,命中則直接取本地磁盤的html(狀態碼為200 from disk(or memory) cache,內存or磁盤);
如果沒有命中強緩存,則會向服務器發起請求(先進行下一步的TCP連接),服務器通過Etag和Last-Modify來與服務器確認返回的響應是否被更改(協商緩存),若無更改則返回狀態碼(304 Not Modified),瀏覽器取本地緩存;
若強緩存和協商緩存都沒有命中則返回請求結果。
不知道你們有沒有注意這樣一件事,你訪問http://baidu.com的時候,每次響應的并非是同一個服務器(IP地址不同),一般大公司都有成百上千臺服務器來支撐訪問,假設只有一個服務器,那它的性能和存儲量要多大才能支撐這樣大量的訪問呢?DNS可以返回一個合適的機器的IP給用戶,例如可以根據每臺機器的負載量,該機器離用戶地理位置的距離等等,這種過程就是DNS負載均衡
TCP 協議通過三次握手建立連接。
翻譯成大白話就是:
為什么是3次?:避免歷史連接,確認客戶端發來的請求是這次通信的人。
為什么不是4次?:3次夠了第四次浪費
建立連接的過程是利用客戶服務器模式,假設主機A為客戶端,主機B為服務器端。
采用三次握手是為了防止失效的連接請求報文段突然又傳送到主機B,因而產生錯誤。失效的連接請求報文段是指:主機A發出的連接請求沒有收到主機B的確認,于是經過一段時間后,主機A又重新向主機B發送連接請求,且建立成功,順序完成數據傳輸。考慮這樣一種特殊情況,主機A第一次發送的連接請求并沒有丟失,而是因為網絡節點導致延遲達到主機B,主機B以為是主機A又發起的新連接,于是主機B同意連接,并向主機A發回確認,但是此時主機A根本不會理會,主機B就一直在等待主機A發送數據,導致主機B的資源浪費。
采用兩次握手不行,原因就是上面說的失效的連接請求的特殊情況。而在三次握手中, client和server都有一個發syn和收ack的過程, 雙方都是發后能收, 表明通信則準備工作OK.
為什么不是四次握手呢? 大家應該知道通信中著名的藍軍紅軍約定, 這個例子說明, 通信不可能100%可靠, 而上面的三次握手已經做好了通信的準備工作, 再增加握手, 并不能顯著提高可靠性, 而且也沒有必要。
第一次握手:
客戶端發送syn包(Seq=x)到服務器,并進入SYN_SEND狀態,等待服務器確認;
第二次握手:
服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(Seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:
客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程中傳送的包里不包含數據,三次握手完畢后,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。
HTTPS=HTTP + 加密 + 認證 + 完整性保護
要先申請CA證書,并安裝在服務器上(一個文件,配置nginx支持監聽443端口開啟ssl并設置證書路徑)
瀏覽器發送請求;
網站從瀏覽器發過來的加密規則中選一組自身也支持的加密算法和hash算法,并向瀏覽器發送帶有公鑰的證書,當然證書還包含了很多信息,如網站地址、證書的頒發機構、過期時間等。
瀏覽器解析證書。
驗證證書的合法性。如頒發機構是否合法、證書中的網站地址是否與訪問的地址一致,若不合法,則瀏覽器提示證書不受信任,若合法,瀏覽器會顯示一個小鎖頭。
若合法,或用戶接受了不合法的證書,瀏覽器會生成一串隨機數的密碼(即密鑰),并用證書中提供的公鑰加密。
使用約定好的hash計算握手消息,并使用生成的隨機數(即密鑰)對消息進行加密,最后將之前生成的所有消息一并發送給網站服務器。
網站服務器解析消息。用已有的私鑰將密鑰解密出來,然后用密鑰解密發過來的握手消息,并驗證是否跟瀏覽器傳過來的一致。然后再用密鑰加密一段握手消息,發送給瀏覽器。
瀏覽器解密并計算握手消息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之后所有的通信數據將由之前瀏覽器生成的隨機密碼并利用對稱加密算法進行加密。這里瀏覽器與網站互相發送加密的握手消息并驗證,目的是為了保證雙方都獲得了一致的密碼,并且可以正常的加密解密數據,為后續真正數據的傳輸做一次測試。
發送HTTP請求
首先科補一個小知識,HTTP的端口為80/8080,而HTTPS的端口為443
發送HTTP請求的過程就是構建HTTP請求報文并通過TCP協議中發送到服務器指定端口 請求報文由請求行,請求抱頭,請求正文組成。
請求行
請求行的格式為Method Request-URL HTTP-Version CRLF eg: GET index.html HTTP/1.1 常用的方法有: GET, POST, PUT, DELETE, OPTIONS, HEAD。
常見的請求方法區別
這里主要展示POST和GET的區別
常見的區別
注意一點你也可以在GET里面藏body,POST里面帶參數
重點區別
GET會產生一個TCP數據包,而POST會產生兩個TCP數據包。
詳細的說就是:
注意一點,并不是所有的瀏覽器都會發送兩次數據包,Firefox就發送一次
請求報頭
請求報頭允許客戶端向服務器傳遞請求的附加信息和客戶端自身的信息。
從圖中可以看出,請求報頭中使用了Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Cookie等字段。Accept用于指定客戶端用于接受哪些類型的信息,Accept-Encoding與Accept類似,它用于指定接受的編碼方式。Connection設置為Keep-alive用于告訴客戶端本次HTTP請求結束之后并不需要關閉TCP連接,這樣可以使下次HTTP請求使用相同的TCP通道,節省TCP連接建立的時間。
請求正文
當使用POST, PUT等方法時,通常需要客戶端向服務器傳遞數據。這些數據就儲存在請求正文中。在請求包頭中有一些與請求正文相關的信息,例如: 現在的Web應用通常采用Rest架構,請求的數據格式一般為json。這時就需要設置Content-Type: application/json。
更重要的事情-HTTP緩存
HTTP屬于客戶端緩存,我們常認為瀏覽器有一個緩存數據庫,用來保存一些靜態文件,下面我們分為以下幾個方面來簡單介紹HTTP緩存
緩存的規則
緩存規則分為強制緩存和協商緩存
強制緩存
當緩存數據庫中有客戶端需要的數據,客戶端直接將數據從其中拿出來使用(如果數據未失效),當緩存服務器沒有需要的數據時,客戶端才會向服務端請求。
又稱對比緩存。客戶端會先從緩存數據庫拿到一個緩存的標識,然后向服務端驗證標識是否失效,如果沒有失效服務端會返回304,這樣客戶端可以直接去緩存數據庫拿出數據,如果失效,服務端會返回新的數據
強制緩存
對于強制緩存,服務器響應的header中會用兩個字段來表明——Expires和Cache-Control。
Expires
Exprires的值為服務端返回的數據到期時間。當再次請求時的請求時間小于返回的此時間,則直接使用緩存數據。但由于服務端時間和客戶端時間可能有誤差,這也將導致緩存命中的誤差,另一方面,Expires是HTTP1.0的產物,故現在大多數使用Cache-Control替代。
Cache-Control
Cache-Control有很多屬性,不同的屬性代表的意義也不同。
協商緩存
協商緩存需要進行對比判斷是否可以使用緩存。瀏覽器第一次請求數據時,服務器會將緩存標識與數據一起響應給客戶端,客戶端將它們備份至緩存中。再次請求時,客戶端會將緩存中的標識發送給服務器,服務器根據此標識判斷。若未失效,返回304狀態碼,瀏覽器拿到此狀態碼就可以直接使用緩存數據了。
對于協商緩存來說,緩存標識我們需要著重理解一下,下面我們將著重介紹它的兩種緩存方案。
Last-Modified
Last-Modified:服務器在響應請求時,會告訴瀏覽器資源的最后修改時間。
從字面上看,就是說:從某個時間節點算起,是否文件被修改了
這兩個的區別是一個是修改了才下載一個是沒修改才下載。
Last-Modified 說好卻也不是特別好,因為如果在服務器上,一個資源被修改了,但其實際內容根本沒發生改變,會因為Last-Modified時間匹配不上而返回了整個實體給客戶端(即使客戶端緩存里有個一模一樣的資源)。為了解決這個問題,HTTP1.1推出了Etag。
Etag
Etag:服務器響應請求時,通過此字段告訴瀏覽器當前資源在服務器生成的唯一標識(生成規則由服務器決定)
但是實際應用中由于Etag的計算是使用算法來得出的,而算法會占用服務端計算的資源,所有服務端的資源都是寶貴的,所以就很少使用Etag了。
緩存的優點
不同刷新的請求執行過程
瀏覽器地址欄中寫入URL,回車
F5
Ctrl+F5
服務器處理請求并返回HTTP報文
它會對TCP連接進行處理,對HTTP協議進行解析,并按照報文格式進一步封裝成HTTP Request對象,供上層使用。這一部分工作一般是由Web服務器去進行,我使用過的Web服務器有Tomcat, Nginx和Apache等等 HTTP報文也分成三份,狀態碼 ,響應報頭和響應報文
狀態碼
狀態碼是由3位數組成,第一個數字定義了響應的類別,且有五種可能取值:
平時遇到比較常見的狀態碼有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500
常見狀態碼區別
200 成功
請求成功,通常服務器提供了需要的資源。
204 無內容
服務器成功處理了請求,但沒有返回任何內容。
301 永久移動
請求的網頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。
302 臨時移動
服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。
304 未修改
自從上次請求后,請求的網頁未修改過。 服務器返回此響應時,不會返回網頁內容。
400 錯誤請求
服務器不理解請求的語法。
401 未授權
請求要求身份驗證。 對于需要登錄的網頁,服務器可能返回此響應。
403 禁止
服務器拒絕請求。
404 未找到
服務器找不到請求的網頁。
422 無法處理
請求格式正確,但是由于含有語義錯誤,無法響應
500 服務器內部錯誤
服務器遇到錯誤,無法完成請求。
響應報頭
常見的響應報頭字段有: Server, Connection...。
響應報文
你從服務器請求的HTML,CSS,JS文件就放在這里面
就是Webkit解析渲染頁面的過程。
這個過程涉及兩個比較重要的概念回流和重繪,DOM結點都是以盒模型形式存在,需要瀏覽器去計算位置和寬度等,這個過程就是回流。等到頁面的寬高,大小,顏色等屬性確定下來后,瀏覽器開始繪制內容,這個過程叫做重繪。瀏覽器剛打開頁面一定要經過這兩個過程的,但是這個過程非常非常非常消耗性能,所以我們應該盡量減少頁面的回流和重繪
這個過程中可能會有dom操作、ajax發起的http網絡請求等。
web-socket、ajax等,這個過程通常是為了獲取數據
setTimeout、setInterval、Promise等宏任務、微任務隊列
當Render Tree中部分或全部元素的尺寸、結構、或某些屬性發生改變時,瀏覽器重新渲染部分或全部文檔的過程稱為回流。
會導致回流的操作:
一些常用且會導致回流的屬性和方法:
當頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:color、background-color、visibility等),瀏覽器會將新樣式賦予給元素并重新繪制它,這個過程稱為重繪。
JS的解析是由瀏覽器的JS引擎完成的。由于JavaScript是單線程運行,也就是說一個時間只能干一件事,干這件事情時其他事情都有排隊,但是有些人物比較耗時(例如IO操作),所以將任務分為同步任務和異步任務,所有的同步任務放在主線程上執行,形成執行棧,而異步任務等待,當執行棧被清空時才去看看異步任務有沒有東西要搞,有再提取到主線程執行,這樣往復循環(冤冤相報何時了,阿彌陀佛),就形成了Event Loop事件循環,下面來看看大人物
先看一段代碼
setTimeout(function(){
console.log('定時器開始啦')
});
new Promise(function(resolve){
console.log('馬上執行for循環啦');
for(var i=0; i < 10000; i++){
i==99 && resolve();
}
}).then(function(){
console.log('執行then函數啦')
});
console.log('代碼執行結束');
結果我想大家都應該知道。主要來介紹JavaScript的解析,至于Promise等下一節再說
JavaScript是一門單線程語言,盡管H5中提出了Web-Worker,能夠模擬實現多線程,但本質上還是單線程,說它是多線程就是扯淡。
既然是單線程,每個事件的執行就要有順序,比如你去銀行取錢,前面的人在進行,后面的就得等待,要是前面的人弄個一兩個小時,估計后面的人都瘋了,因此,瀏覽器的JS引擎處理JavaScript時分為同步任務和異步任務
這張圖我們可以清楚看到
js引擎存在monitoring process進程,會持續不斷的檢查主線程執行棧是否為空,一旦為空,就會去Event Queue那里檢查是否有等待被調用的函數。 估計看完這些你對事件循環有一定的了解,但是事實上我們看對的沒這么簡單,通常我們會看到Promise,setTimeout,process.nextTick(),這個時候你和我就懵逼。
除了同步任務和異步任務,我們還分為宏任務和微任務,常見的有以下幾種
不同任務會進入不同的任務隊列來執行。 JS引擎開始工作后,先在宏任務中開始第一次循環(script里面先執行,不過我喜歡把它拎出來,直接稱其進入執行棧),當主線程執行棧全部任務被清空后去微任務看看,如果有等待執行的任務,執行全部的微任務(其實將其回調函數推入執行棧來執行),再去宏任務找最先進入隊列的任務執行,執行這個任務后再去主線程執行任務(例如執行```console.log("hello world")這種任務),執行棧被清空后再去微任務,這樣往復循環(冤冤相報何時了)
Tip:微任務會全部執行,而宏任務會一個一個來執行
下面來看一段代碼
setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
resolve();
}).then(function() {
console.log('then');
})
console.log('console');
我們看看它的執行情況
具體的執行過程大致就是這樣。
文本標記語言(HTML)是用于在 Internet 上顯示 Web 頁面的主要標記語言。換句話說,網頁由 HTML 組成,用于通過 Web 瀏覽器顯示文本,圖像或其他資源。所有 HTML 都是純文本,這意味著它不是編譯的,可以由人類閱讀。HTML 文件的文件擴展名為.htm 或.html。
自1990年以來,HTML就一直被用作WWW的信息表示語言,使用HTML語言描述的文件需要通過WWW瀏覽器顯示出效果。HTML是一種建立網頁文件的語言,通過標記式的指令(Tag),將影像、聲音、圖片、文字動畫、影視等內容顯示出來。事實上,每一個HTML文檔都是一種靜態的網頁文件,這個文件里面包含了HTML指令代碼,這些指令代碼并不是一種程序語言,只是一種排版網頁中資料顯示位置的標記結構語言,易學易懂,非常簡單。
HTML的普遍應用就是帶來了超文本的技術―通過單擊鼠標從一個主題跳轉到另一個主題,從一個頁面跳轉到另一個頁面,與世界各地主機的文件鏈接超文本傳輸協議規定了瀏覽器在運行HTML文檔時所遵循的規則和進行的操作。HTTP協議的制定使瀏覽器在運行超文本時有了統一的規則和標準。
萬維網(world wide web)上的一個超媒體文檔稱之為一個頁面(外語:page)。作為一個組織或者個人在萬維網上放置開始點的頁面稱為主頁(外語:Homepage)或首頁,主頁中通常包括有指向其他相關頁面或其他節點的指針(超級鏈接),所謂超級鏈接,就是一種統一資源定位器(Uniform Resource Locator,外語縮寫:URL)指針,通過激活(點擊)它,可使瀏覽器方便地獲取新的網頁。這也是HTML獲得廣泛應用的最重要的原因之一。在邏輯上將視為一個整體的一系列頁面的有機集合稱為網站(Website或Site)。超級文本標記語言(英文縮寫:HTML)是為“網頁創建和其它可在網頁瀏覽器中看到的信息”設計的一種標記語言。
當 Web 開發人員構建應用程序時,工作在服務器上執行,原始 HTML 將發送給用戶。使用 AJAX 等技術,服務器端開發和客戶端開發之間的界限很模糊。
HTML 從未被設計用于當今存在的 Web,因為它只是一種在控制和設計方面具有嚴重限制的標記語言。已經使用了許多技術來解決這個問題 – 最重要的是層疊樣式表(CSS)。
長期解決方案是(或希望是)HTML5,它是下一代 HTML,允許更多的控制和交互性。與 Web 上的任何開發一樣,遷移到標準是一個緩慢而艱巨的過程,Web 開發人員和設計人員必須使用當前和支持的技術,這意味著基本 HTML 將繼續使用一段時間。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。