金九銀十,又是一波跑路。趁著有空把前端基礎和面試相關的知識點都系統的學習一遍,參考一些權威的書籍和優秀的文章,最后加上自己的一些理解,總結出來這篇文章。適合復習和準備面試的同學,其中的知識點包括:
String、Number、Boolean、Null、Undefined、Symbol、BigInt、Object
兩者都是存放數據的地方。
棧(stack)是自動分配的內存空間,它存放基本類型的值和引用類型的內存地址。
堆(heap)是動態分配的內存空間,它存放引用類型的值。
JavaScript 不允許直接操作堆空間的對象,在操作對象時,實際操作是對象的引用,而存放在棧空間中的內存地址就起到指向的作用,通過內存地址找到堆空間中的對應引用類型的值。
JavaScript 作為一個弱類型語言,因使用靈活的原因,在一些場景中會對類型進行自動轉換。
常見隱式類型轉換場景有3種:運算、取反、比較
運算的隱式類型轉換會將運算的成員轉換為 number 類型。
基本類型轉換:
true + false // 1
null + 10 // 10
false + 20 // 20
undefined + 30 // NaN
1 + '2' // "12"
NaN + '' // "NaN"
undefined + '' // "undefined"
null + '' // "null"
'' - 3 // -3
引用類型轉換:
[1] + 10 // "110"
[] + 20 // "20"
[1,2] + 20 // "1,220"
[20] - 10 // 10
[1,2] - 10 // NaN
({}) + 10 // "[object Object]10"
({}) - 10 // NaN
解析引用類型轉換過程:
[1,2] + 20
// 過程:
[1,2].toString() // '1,2'
'1,2' + 20 // '1,220'
[20] - 10
// 過程
[20].toString() // '20'
Number('20') // 20
20 - 10 // 10
取反的隱式類型轉換會將運算的成員轉換為 boolean 類型。
這個隱式類型轉換比較簡單,就是將值轉為布爾值再取反:
![] // false
!{} // false
!false // true
通常為了快速獲得一個值的布爾值類型,可以取反兩次:
!![] // true
!!0 // false
比較分為 嚴格比較===和 非嚴格比較==,由于===會比較類型,不會進行類型轉換。這里只討論==。
比較的隱式類型轉換基本會將運算的成員轉換為 number 類型。
undefined==null // true
''==0 // true
true==1 // true
'1'==true // true
[1]=='1' // true
[1,2]=='1,2' // true
({})=='[object Object]' // true
預編譯發生在 JavaScript 代碼執行前,對代碼進行語法分析和代碼生成,初始化的創建并存儲變量,為執行代碼做好準備。
預編譯過程:
例子:
function foo(x, y) {
console.log(x)
var x=10
console.log(x)
function x(){}
console.log(x)
}
foo(20, 30)
// 1. 創建AO對象
AO {}
// 2. 尋找形參和變量聲明賦值為 undefined
AO {
x: undefined
y: undefined
}
// 3. 實參形參相統一
AO {
x: 20
y: 30
}
// 4. 函數聲明提升
AO {
x: function x(){}
y: 30
}
編譯結束后代碼開始執行,第一個 x 從 AO 中取值,輸出是函數x;x 被賦值為 10,第二個 x 輸出 10;函數x 已被聲明提升,此處不會再賦值 x,第三個 x 輸出 10。
作用域能保證對有權訪問的所有變量和函數的有序訪問,是代碼在運行期間查找變量的一種規則。
函數在運行時會創建屬于自己的作用域,將內部的變量和函數定義“隱藏”起來,外部作用域無法訪問包裝函數內部的任何內容。
在ES6之前創建塊級作用域,可以使用 with 或 try/catch。而在ES6引入 let 關鍵字后,讓塊級作用域聲明變得更簡單。let 關鍵字可以將變量綁定到所在的任意作用域中(通常是{...}內部)。
{
let num=10
}
console.log(num) // ReferenceError: num is not defined
一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法行為,在不設置參數默認值時,是不會出現的。
let x=1;
function f(x, y=x) {
console.log(y);
}
f(2) // 2
參數y的默認值等于變量x。調用函數f時,參數形成一個單獨的作用域。在這個作用域里面,默認值變量x指向第一個參數x,而不是全局變量x,所以輸出是2。
let x=1;
function foo(x, y=function() { x=2; }) {
x=3;
y();
console.log(x);
}
foo() // 2
x // 1
y 的默認是一個匿名函數,匿名函數內的x指向同一個作用域的第一個參數x。函數foo的內部變量x就指向第一個參數x,與匿名函數內部的x是一致的。y函數執行對參數x重新賦值,最后輸出的就是2,而外層的全局變量x依然不受影響。
閉包的本質就是作用域問題。當函數可以記住并訪問所在作用域,且該函數在所處作用域之外被調用時,就會產生閉包。
簡單點說,一個函數內引用著所在作用域的變量,并且它被保存到其他作用域執行,引用變量的作用域并沒有消失,而是跟著這個函數。當這個函數執行時,就可以通過作用域鏈查找到變量。
let bar
function foo() {
let a=10
// 函數被保存到了外部
bar=function () {
// 引用著不是當前作用域的變量a
console.log(a)
}
}
foo()
// bar函數不是在本身所處的作用域執行
bar() // 10
優點:私有變量或方法、緩存
缺點:閉包讓作用域鏈得不到釋放,會導致內存泄漏
JavaScript 中的對象有一個特殊的內置屬性 prototype(原型),它是對于其他對象的引用。當查找一個變量時,會優先在本身的對象上查找,如果找不到就會去該對象的 prototype 上查找,以此類推,最終以 Object.prototype 為終點。多個 prototype 連接在一起被稱為原型鏈。
原型繼承的方法有很多種,這里不會全部提及,只記錄兩種常用的方法。
function inherit(Target, Origin){
function F() {};
F.prototype=Origin.prototype;
Target.prototype=new F();
// 還原 constuctor
Target.prototype.constuctor=Target;
// 記錄繼承自誰
Target.prototype.uber=Origin.prototype;
}
圣杯模式的好處在于,使用中間對象隔離,子級添加屬性時,都會加在這個對象里面,不會對父級產生影響。而查找屬性是沿著 __proto__ 查找,可以順利查找到父級的屬性,實現繼承。
使用:
function Person() {
this.name='people'
}
Person.prototype.sayName=function () { console.log(this.name) }
function Child() {
this.name='child'
}
inherit(Child, Person)
Child.prototype.age=18
let child=new Child()
class Person {
constructor() {
this.name='people'
}
sayName() {
console.log(this.name)
}
}
class Child extends Person {
constructor() {
super()
this.name='child'
}
}
Child.prototype.age=18
let child=new Child()
Class 可以通過 extends 關鍵字實現繼承,這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多。
let str='hello'
str.split('')
基本類型按道理說是沒有屬性和方法,但是在實際操作時,我們卻能從基本類型調用方法,就像一個字符串能調用 split 方法。
為了方便操作基本類型值,每當讀取一個基本類型值的時候,后臺會創建一個對應的基本包裝類型的對象,從而讓我們能夠調用方法來操作這些數據。大概過程如下:
let str=new String('hello')
str.split('')
str=null
this是函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用。我理解的this是函數的調用者對象,當在函數內使用this,可以訪問到調用者對象上的屬性和方法。
this綁定的四種情況:
優先級new綁定最高,最后到默認綁定。
注意點:構造函數內出現return,如果返回基本類型,則提前結束構造過程,返回實例對象;如果返回引用類型,則返回該引用類型。
// 返回基本類型
function Foo(){
this.name='Joe'
return 123
this.age=20
}
new Foo() // Foo {name: "Joe"}
// 返回引用類型
function Foo(){
this.name='Joe'
return [123]
this.age=20
}
new Foo() // [123]
三者作用都是改變this指向的。
call 和 apply 改變 this 指向并調用函數,它們兩者區別就是傳參形式不同,前者的參數是逐個傳入,后者傳入數組類型的參數列表。
bind 改變 this 并返回一個函數引用,bind 多次調用是無效的,它改變的 this 指向只會以第一次調用為準。
Function.prototype.mycall=function () {
if(typeof this !=='function'){
throw 'caller must be a function'
}
let othis=arguments[0] || window
othis._fn=this
let arg=[...arguments].slice(1)
let res=othis._fn(...arg)
Reflect.deleteProperty(othis, '_fn') //刪除_fn屬性
return res
}
apply 實現同理,修改傳參形式即可
Function.prototype.mybind=function (oThis) {
if(typeof this !='function'){
throw 'caller must be a function'
}
let fThis=this
//Array.prototype.slice.call 將類數組轉為數組
let arg=Array.prototype.slice.call(arguments,1)
let NOP=function(){}
let fBound=function(){
let arg_=Array.prototype.slice.call(arguments)
// new 綁定等級高于顯式綁定
// 作為構造函數調用時,保留指向不做修改
// 使用 instanceof 判斷是否為構造函數調用
return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
}
// 維護原型
if(this.prototype){
NOP.prototype=this.prototype
fBound.prototype=new NOP()
}
return fBound
}
常用:let、const、擴展運算符、模板字符串、對象解構、箭頭函數、默認參數、Promise
數據結構:Set、Map、Symbol
其他:Proxy、Reflect
Set:
WeakSet:
Map:
WeakMap:
Promise 是ES6中新增的異步編程解決方案,避免回調地獄問題。Promise 對象是通過狀態的改變來實現通過同步的流程來表示異步的操作, 只要狀態發生改變就會自動觸發對應的函數。
Promise對象有三種狀態,分別是:
狀態一旦改變既不可逆,可以通過函數來監聽 Promise 狀態的變化,成功執行 then 函數的回調,失敗執行 catch 函數的回調
淺拷貝是值的復制,對于對象是內存地址的復制,目標對象的引用和源對象的引用指向的是同一塊內存空間。如果其中一個對象改變,就會影響到另一個對象。
常用淺拷貝的方法:
let arr=[{a:1}, {b:2}]
let newArr=arr1.slice()
let newArr=[...arr1]
深拷貝是將一個對象從內存中完整的拷貝一份出來,對象與對象間不會共享內存,而是在堆內存中新開辟一個空間去存儲,所以修改新對象不會影響原對象。
常用的深拷貝方法:
JSON.parse(JSON.stringify(obj))
function deepClone(obj, map=new WeakMap()) {
if (obj===null || typeof obj !=="object") return obj;
const type=Object.prototype.toString.call(obj).slice(8, -1)
let strategy={
Date: (obj)=> new Date(obj),
RegExp: (obj)=> new RegExp(obj),
Array: clone,
Object: clone
}
function clone(obj){
// 防止循環引用,導致棧溢出,相同引用的對象直接返回
if (map.get(obj)) return map.get(obj);
let target=new obj.constructor();
map.set(obj, target);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
target[key]=deepClone(obj[key], map);
}
}
return target;
}
return strategy[type] && strategy[type](obj)
}
事件委托也叫做事件代理,是一種dom事件優化的手段。事件委托利用事件冒泡的機制,只指定一個事件處理程序,就可以管理某一類型的所有事件。
假設有個列表,其中每個子元素都會有個點擊事件。當子元素變多時,事件綁定占用的內存將會成線性增加,這時候就可以使用事件委托來優化這種場景。代理的事件通常會綁定到父元素上,而不必為每個子元素都添加事件。
<ul @click="clickHandler">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
clickHandler(e) {
// 點擊獲取的子元素
let target=e.target
// 輸出子元素內容
consoel.log(target.textContent)
}
防抖用于減少函數調用次數,對于頻繁的調用,只執行這些調用的最后一次。
/**
* @param {function} func - 執行函數
* @param {number} wait - 等待時間
* @param {boolean} immediate - 是否立即執行
* @return {function}
*/
function debounce(func, wait=300, immediate=false){
let timer, ctx;
let later=(arg)=> setTimeout(()=>{
func.apply(ctx, arg)
timer=ctx=null
}, wait)
return function(...arg){
if(!timer){
timer=later(arg)
ctx=this
if(immediate){
func.apply(ctx, arg)
}
}else{
clearTimeout(timer)
timer=later(arg)
}
}
}
節流用于減少函數請求次數,與防抖不同,節流是在一段時間執行一次。
/**
* @param {function} func - 執行函數
* @param {number} delay - 延遲時間
* @return {function}
*/
function throttle(func, delay){
let timer=null
return function(...arg){
if(!timer){
timer=setTimeout(()=>{
func.apply(this, arg)
timer=null
}, delay)
}
}
}
Currying(柯里化)是把接受多個參數的函數變換成接受一個單一參數的函數,并且返回接受余下的參數而且返回結果的新函數的技術。
通用柯里化函數:
function currying(fn, arr=[]) {
let len=fn.length
return (...args)=> {
let concatArgs=[...arr, ...args]
if (concatArgs.length < len) {
return currying(fn, concatArgs)
} else {
return fn.call(this, ...concatArgs)
}
}
}
使用:
let sum=(a,b,c,d)=> {
console.log(a,b,c,d)
}
let newSum=currying(sum)
newSum(1)(2)(3)(4)
優點:
堆分為新生代和老生代,分別由副垃圾回收器和主垃圾回收器來負責垃圾回收。
一般剛使用的對象都會放在新生代,它的空間比較小,只有幾十MB,新生代里還會劃分出兩個空間:form空間和to空間。
對象會先被分配到form空間中,等到垃圾回收階段,將form空間的存活對象復制到to空間中,對未存活對象進行回收,之后調換兩個空間,這種算法稱之為 “Scanvage”。
新生代的內存回收頻率很高、速度也很快,但空間利用率較低,因為讓一半的內存空間處于“閑置”狀態。
老生代的空間較大,新生代經過多次回收后還存活的對象會被送到老生代。
老生代使用“標記清除”的方式,從根元素開始遍歷,將存活對象進行標記。標記完成后,對未標記的對象進行回收。
經過標記清除之后的內存空間會產生很多不連續的碎片空間,導致一些大對象無法存放進來。所以在回收完成后,會對這些不連續的碎片空間進行整理。
定義:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
JavaScript 作為一門無類的語言,傳統的單例模式概念在 JavaScript 中并不適用。稍微轉換下思想:單例模式確保只有一個對象,并提供全局訪問。
常見的應用場景就是彈窗組件,使用單例模式封裝全局彈窗組件方法:
import Vue from 'vue'
import Index from './index.vue'
let alertInstance=null
let alertConstructor=Vue.extend(Index)
let init=(options)=>{
alertInstance=new alertConstructor()
Object.assign(alertInstance, options)
alertInstance.$mount()
document.body.appendChild(alertInstance.$el)
}
let caller=(options)=>{
// 單例判斷
if(!alertInstance){
init(options)
}
return alertInstance.show(()=>alertInstance=null)
}
export default {
install(vue){
vue.prototype.$alert=caller
}
}
無論調用幾次,組件也只實例化一次,最終獲取的都是同一個實例。
定義:定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。
策略模式是開發中最常用的設計模式,在一些場景下如果存在大量的 if/else,且每個分支點的功能獨立,這時候就可以考慮使用策略模式來優化。
就像就上面手寫深拷貝就用到策略模式來實現:
function deepClone(obj, map=new WeakMap()) {
if (obj===null || typeof obj !=="object") return obj;
const type=Object.prototype.toString.call(obj).slice(8, -1)
// 策略對象
let strategy={
Date: (obj)=> new Date(obj),
RegExp: (obj)=> new RegExp(obj),
Array: clone,
Object: clone
}
function clone(obj){
// 防止循環引用,導致棧溢出,相同引用的對象直接返回
if (map.get(obj)) return map.get(obj);
let target=new obj.constructor();
map.set(obj, target);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
target[key]=deepClone(obj[key], map);
}
}
return target;
}
return strategy[type] && strategy[type](obj)
}
這樣的代碼看起來會更簡潔,只需要維護一個策略對象,需要新功能就添加一個策略。由于策略項是單獨封裝的方法,也更易于復用。
定義:為一個對象提供一個代用品,以便控制對它的訪問。
當不方便直接訪問一個對象或者不滿足需要的時候,提供一個代理對象來控制對這個對象的訪問,實際訪問的是代理對象,代理對象對請求做出處理后,再轉交給本體對象。
使用緩存代理請求數據:
function getList(page) {
return this.$api.getList({
page
}).then(res=> {
this.list=res.data
return res
})
}
// 代理getList
let proxyGetList=(function() {
let cache={}
return async function(page) {
if (cache[page]) {
return cache[page]
}
let res=await getList.call(this, page)
return cache[page]=res.data
}
})()
上面的場景是常見的分頁需求,同一頁的數據只需要去后臺獲取一次,并將獲取到的數據緩存起來,下次再請求同一頁時,便可以直接使用之前的數據。
定義:它定義對象間的一種一對多的依賴關系,當一個對象的狀態發送改變時,所有依賴于它的對象都將得到通知。
發布訂閱模式主要優點是解決對象間的解耦,它的應用非常廣泛,既可以用在異步編程中,也可以幫助我們完成松耦合的代碼編寫。像 eventBus 的通信方式就是發布訂閱模式。
let event={
events: [],
on(key, fn){
if(!this.events[key]) {
this.events[key]=[]
}
this.events[key].push(fn)
},
emit(key, ...arg){
let fns=this.events[key]
if(!fns || fns.length==0){
return false
}
fns.forEach(fn=> fn.apply(this, arg))
}
}
上面只是發布訂閱模式的簡單實現,還可以為其添加 off 方法來取消監聽事件。在 Vue 中,通常是實例化一個新的 Vue 實例來做發布訂閱中心,解決組件通信。而在小程序中可以手動實現發布訂閱模式,用于解決頁面通信的問題。
定義:動態地為某個對象添加一些額外的職責,而不會影響對象本身。
裝飾器模式在開發中也是很常用的設計模式,它能夠在不影響源代碼的情況下,很方便的擴展屬性和方法。比如以下應用場景是提交表單。
methods: {
submit(){
this.$api.submit({
data: this.form
})
},
// 為提交表單添加驗證功能
validateForm(){
if(this.form.name==''){
return
}
this.submit()
}
}
想象一下,如果你剛接手一個項目,而 submit 的邏輯很復雜,可能還會牽扯到很多地方。冒然的侵入源代碼去擴展功能會有風險,這時候裝飾器模式就幫上大忙了。
MVVM 對應 3個組成部分,Model(模型)、View(視圖) 和 ViewModel(視圖模型)。
View 不能和 Model 直接通信,它們只能通過 ViewModel 通信。Model 和 ViewModel 之間的交互是雙向的,ViewModel 通過雙向數據綁定把 View 層和 Model 層連接起來,因此 View 數據的變化會同步到 Model 中,而 Model 數據的變化也會立即反應到 View 上。
題外話,你可能不知道 Vue 不完全是 MVVM 模式:
嚴格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 在組件提供了 $refs 這個屬性,讓 Model 可以直接操作 View,違反了這一規定。
流程主要分為三個部分:
當一個組件被定義,data 必須聲明為返回一個初始數據對象的函數,因為組件可能被用來創建多個實例。如果 data 仍然是一個純粹的對象,則所有的實例將共享引用同一個數據對象!通過提供 data 函數,每次創建一個新實例后,我們能夠調用 data 函數,從而返回初始數據的一個全新副本數據對象。
JavaScript 中的對象作為引用類型,如果是創建多個實例,直接使用對象會導致實例的共享引用。而這里創建多個實例,指的是組件復用的情況。因為在編寫組件時,是通過 export 暴露出去的一個對象,如果組件復用的話,多個實例都是引用這個對象,就會造成共享引用。使用函數返回一個對象,由于是不同引用,自然可以避免這個問題發生。
“計算屬性Watcher”會帶有一個 dirty 的屬性,在初始化取值完成后,會將 dirty 設置為 false。只要依賴屬性不更新,dirty 永遠為 false,重復取值也不會再去執行求值函數,而是直接返回結果,從而實現緩存。相反,依賴屬性更新會將“計算屬性 Watcher”的 dirty 設置為 true,在頁面渲染對計算屬性取值時,再次觸發求值函數更新計算屬性。
Object.defineProperty(target, key, {
get() {
const watcher=this._computedWatchers && this._computedWatchers[key]
// 計算屬性緩存
if (watcher.dirty) {
// 計算屬性求值
watcher.evaluate()
}
return watcher.value
}
})
雙向綁定是視圖變化會反映到數據,數據變化會反映到視圖,v-model 就是個很好理解的例子。其實主要考查的還是響應式原理,響應式原理共包括3個主要成員,Observer 負責監聽數據變化,Dep 負責依賴收集,Watcher 負責數據或視圖更新,我們常說的收集依賴就是收集 Watcher。
響應式原理主要工作流程如下:
Vue 內部重寫數組原型鏈,當數組發生變化時,除了執行原生的數組方法外,還會調用 dep.notify 通知 Watcher 更新。觸發數組更新的方法共7種:
keep-alive 是 Vue 的內置組件,同時也是一個抽象組件,它主要用于組件緩存。當組件切換時會將組件的VNode緩存起來,等待下次重新激活時,再將緩存的組件VNode渲染出來,從而實現緩存。
常用的兩個屬性 include 和 exclude,支持字符串、正則和數組的形式,允許組件有條件的進行緩存。還有 max 屬性,用于設置最大緩存數。
兩個生命周期 activated 和 deactivated,在組件激活和失活時觸發。
keep-alive 的緩存機制運用LRU(Least Recently Used)算法,
在下次 dom 更新結束之后執行延遲回調。nextTick 主要使用了宏任務和微任務。根據執行環境分別嘗試采用:
nextTick 主要用于內部 Watcher 的異步更新,對外我們可以使用 Vue.nextTick 和 vm.$nextTick。在 nextTick 中可以獲取更新完成的 dom。
所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外變更父級組件的狀態,從而導致你的應用的數據流向難以理解。
單向數據流只允許數據由父組件傳遞給子組件,數據只能由父組件更新。當數據傳遞到多個子組件,而子組件能夠在其內部更新數據時,在主觀上很難知道是哪個子組件更新了數據,導致數據流向不明確,從而增加應用調試的難度。
但子組件更新父組件數據的場景確實存在,有3種方法可以使用:
Object.definedProperty 只能檢測到屬性的獲取和設置,對于新增和刪除是沒辦法檢測的。在數據初始化時,由于不知道哪些數據會被用到,Vue 是直接遞歸觀測全部數據,這會導致性能多余的消耗。
Proxy 劫持整個對象,對象屬性的增加和刪除都能檢測到。Proxy 并不能監聽到內部深層的對象變化,因此 Vue 3.0 的處理方式是在 getter 中去遞歸響應式,只有真正訪問到的內部對象才會變成響應式,而不是無腦遞歸,在很大程度上提升了性能。
路由懶加載是性能優化的一種手段,在編寫代碼時可以使用 import() 引入路由組件,使用懶加載的路由會在打包時單獨出來成一個 js 文件,可以使用 webpackChunkName 自定義包名。在項目上線后,懶加載的 js 文件不會在第一時間加載,而是在訪問到對應的路由時,才會動態創建 script 標簽去加載這個 js 文件。
{
path:'users',
name:'users',
component:()=> import(/*webpackChunkName: "users"*/ '@/views/users'),
}
路由進入前調用
const router=new VueRouter({ ... })
router.beforeEach((to, from, next)=> {
// ...
})
在所有組件內守衛和異步組件被解析之后調用
router.beforeResolve((to, from, next)=> {
// ...
})
路由在確認后調用
router.afterEach((to, from)=> {
// ...
})
路由進入前調用,beforeEnter 在 beforeEach 之后執行
const router=new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next)=> {
// ...
}
}
]
})
路由確認前調用,組件實例還沒被創建,不能獲取組件實例 this
beforeRouteEnter (to, from, next) {
// ...
// 可以通過回調訪問實例
next(vm=> {
// vm 為組件實例
})
},
路由改變時調用,可以訪問組件實例
beforeRouteUpdate (to, from, next) {
// ...
},
離開該組件的對應路由時調用,可以訪問組件實例 this
beforeRouteLeave (to, from, next) {
// ...
}
vue-router原理是更新視圖而不重新請求頁面。vue-router共有3種模式:hash模式、history模式、abstract模式。
hash模式使用 hashchange 監聽地址欄的hash值的變化,加載對應的頁面。每次的hash值變化后依然會在瀏覽器留下歷史記錄,可以通過瀏覽器的前進后退按鈕回到上一個頁面。
history模式基于History Api實現,使用 popstate 監聽地址欄的變化。使用 pushState 和 replaceState 修改url,而無需加載頁面。但是在刷新頁面時還是會向后端發起請求,需要后端配合將資源定向回前端,交由前端路由處理。
不涉及和瀏覽器地址的相關記錄。通過數組維護模擬瀏覽器的歷史記錄棧。
跨模塊調用是指當前命名空間模塊調用全局模塊或者另一個命名空間模塊。在調用 dispatch 和 commit 時設置第三個參數為 {root:true}。
modules: {
foo: {
namespaced: true,
actions: {
someAction ({ dispatch, commit, getters, rootGetters }) {
// 調用自己的action
dispatch('someOtherAction') // -> 'foo/someOtherAction'
// 調用全局的action
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
// 調用其他模塊的action
dispatch('user/someOtherAction', null, { root: true }) // -> 'user/someOtherAction'
},
someOtherAction (ctx, payload) { ... }
}
}
}
vuex存儲的狀態在頁面刷新后會丟失,使用持久化技術能保證頁面刷新后狀態依然存在。
這里只記錄常用的兩種模塊:CommonJS模塊、ES6模塊。
Node.js 采用 CommonJS 模塊規范,在服務端運行時是同步加載,在客戶端使用需要編譯后才可以運行。
module.exports 不為空:
// nums.js
exports.a=1
module.exports={
b: 2
}
exports.c=3
let nums=require('./nums.js') // { b: 2 }
module.exports 為空:
// nums.js
exports.a=1
exports.c=3
let nums=require('./nums.js') // { a: 1, c: 3 }
值拷貝的體現:
// nums.js
let obj={
count: 10
}
let count=20
function addCount() {
count++
}
function getCount() {
return count
}
function addObjCount() {
obj.count++
}
module.exports={ count, obj, addCount, getCount, addObjCount }
let { count, obj, addCount, getCount, addObjCount }=require('./nums.js')
// 原始類型不受影響
console.log(count) // 20
addCount()
console.log(count) // 20
// 如果想獲取到變化的值,可以使用函數返回
console.log(getCount()) // 21
// 引用類型會被改變
console.log(obj) // { count: 10 }
addObjCount()
console.log(obj) // { count: 11 }
ES6 模塊的設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。
// nums.js
export let count=20
export function addCount() {
count++
}
export default {
other: 30
}
// 同時引入 export default 和 export 的變量
import other, { count, addCount } from './async.js'
console.log(other) // { other: 30 }
console.log(count) // 20
addCount()
console.log(count) // 21
數據變化過程:字節 → 字符 → 令牌 → 樹 → 頁面
在布局完成后,對DOM布局進行修改(比如大小或位置),會引起頁面重新計算布局,這個過程稱為“回流”。
對DOM進行不影響布局的修改引起的屏幕局部繪制(比如背景顏色、字體顏色),這個過程稱為“重繪”。
回流一定會引起重繪,而重繪不一定會引起回流。由于回流需要重新計算節點布局,回流的渲染耗時會高于重繪。
對于回流重繪,瀏覽器本身也有優化策略,瀏覽器會維護一個隊列,將回流重繪操作放入隊列中,等隊列到達一定時間,再按順序去一次性執行隊列的操作。
但是也有例外,有時我們需要獲取某些樣式信息,例如:
offsetTop,offsetLeft,offsetWidth,offsetHeight,scrollTop/Left/Width/Height,clientTop/Left/Width/Height,getComputedStyle(),或者 IE 的 currentStyle。
這時,瀏覽器為了反饋準確的信息,需要立即回流重繪一次,所以可能導致隊列提前執行。
在瀏覽器的實現上,諸如渲染任務、JavaScript 腳本執行、User Interaction(用戶交互)、網絡處理都跑在同一個線程上,當執行其中一個類型的任務的時候意味著其他任務的阻塞,為了有序的對各個任務按照優先級進行執行瀏覽器實現了我們稱為 Event Loop 調度流程。
簡單來說,Event Loop 就是執行代碼、收集和處理事件以及執行隊列中子任務的一個過程。
在一次新的事件循環的過程中,遇到宏任務時,宏任務將被加入任務隊列,但需要等到下一次事件循環才會執行。
常見宏任務:setTimeout、setInterval、requestAnimationFrame
當前事件循環的任務隊列為空時,微任務隊列中的任務就會被依次執行。在執行過程中,如果遇到微任務,微任務被加入到當前事件循環的微任務隊列中。簡單來說,只要有微任務就會繼續執行,而不是放到下一個事件循環才執行。
微任務隊列屬于任務運行環境內的一員,并非處于全局的位置。也就是說,每個任務都會有一個微任務隊列。
常見微任務:Promise.then、Promise.catch、MutationObserver
在當前任務運行環境內,微任務總是先于宏任務執行;
requestAnimationFrame 回調在頁面渲染之前調用,適合做動畫;
requestIdleCallback 在渲染屏幕之后調用,可以使用它來執行一些不太重要的任務。
源是由 URL 中協議、主機名(域名)以及端口共同組成的部分。
同源策略是瀏覽器的行為,為了保護本地數據不被JavaScript代碼獲取回來的數據污染,它是存在于瀏覽器最核心也最基本的安全功能。
所謂同源指的是:協議、域名、端口號必須一致,只要有一個不相同,那么就是“跨源”。
最常見的同源策略是因為域名不同,也就是常說的“跨域”。一般分為請求跨域和頁面跨域。
常用方法是CORS和代理轉發。
對于CORS請求,瀏覽器將其分成兩個類型:簡單請求和非簡單請求。
簡單請求符合下面 2 個特征:
任意一條要求不符合的即為非簡單請求。常見是自定義 header,例如將token 設置到請求頭。
在處理非簡單請求時,瀏覽器會先發出“預檢請求”,預檢請求為OPTIONS方法,以獲知服務器是否允許該實際請求,避免跨域請求對服務器產生預期外的影響。如果預檢請求返回200允許通過,才會發真實的請求。
預檢請求并非每次都需要發送,可以使用 Access-Control-Max-Age 設置緩存時間進行優化,減少請求發送。
增加頭部設定,頭部內容以鍵值對的形式設置。請求頭部通過 Accept 字段來告訴服務端可以接收的文件類型,響應頭部再通過 Content-Type 字段來告訴瀏覽器返回文件的類型。
HTTP1.0中每次通信都需要經歷建立連接、傳輸數據和斷開連接三個階段,這會增加大量網絡開銷。
HTTP1.1增加持久化連接,即連接傳輸完畢后,TCP連接不會馬上關閉,而是其他請求可以復用連接。這個連接保持到瀏覽器或者服務器要求斷開連接為止。
HTTP1.1雖然減少連接帶來的性能消耗,但是請求最大并發受到限制,同一域下的HTTP連接數根據瀏覽器不同有所變化,一般是6 ~ 8個。而且一個TCP連接同一時刻只能處理一個請求,當前請求未結束之前,其他請求只能處于阻塞狀態。
HTTP2.0中增加“多路復用”的機制,不再受限于瀏覽器的連接數限制。基于二進制分幀,客戶端發送的數據會被分割成帶有編號的碎片(二進制幀),然后將這些碎片同時發送給服務端,服務端接收到數據后根據編號再合并成完整的數據。服務端返回數據也同樣遵循這個過程。
第一次握手:客戶端向服務端發起連接請求報文,報文中帶有一個連接標識(SYN);
第二次握手:服務端接收到客戶端的報文,發現報文中有連接標識,服務端知道是一個連接請求,于是給客戶端回復確認報文(帶有SYN標識);
第三次握手:客戶端收到服務端回復確認報文,得知服務端允許連接,于是客戶端回復確認報文給服務端,服務端收到客戶端的回復報文后,正式建立TCP連接;
如果是兩次握手,在第二次握手出現確認報文丟失,客戶端不知道服務端是否準備好了,這種情況下客戶端不會給服務端發數據,也會忽略服務端發過來的數據。
如果是三次握手,在第三次握手出現確認報文丟失,服務端在一段時間沒有收到客戶端的回復報文就會重新第二次握手,客戶端收到重復的報文會再次給服務端發送確認報文。
三次握手主要考慮是丟包重連的問題。
第一次揮手:客戶端向服務端發出連接釋放報文,報文中帶有一個連接釋放標識(FIN)。此時客戶端不能再發送數據,但是可以正常接收數據;
第二次揮手:服務端接收到客戶端的報文,知道是一個連接釋放請求。服務端給客戶端回復確認報文,但要注意這個回復報文未帶有FIN標識。此時服務端處于關閉等待狀態,這個狀態還要持續一段時間,因為服務端可能還有數據沒發完;
第三次揮手:服務端將最后的數據發送完畢后,給客戶端回復確認報文(帶有FIN標識),這個才是通知客戶端可以釋放連接的報文;
第四次揮手:客戶端收到服務端回復確認報文后,于是客戶端回復確認報文給服務端。而服務端一旦收到客戶端發出的確認報文就會立馬釋放TCP連接,所以服務端結束TCP連接的時間要比客戶端早一些。
服務端需要確保數據完整性,只能先回復客戶端確認報文告訴客戶端我收到了報文,進入關閉等待狀態。服務端在數據發送完畢后,才回復FIN報文告知客戶端數據發送完了,可以斷開了,由此多了一次揮手過程。
HTTPS之所以比HTTP安全,是因為對傳輸內容加密。HTTPS加密使用對稱加密和非對稱加密。
對稱加密:雙方共用一把鑰匙,可以對內容雙向加解密。但是只要有人和服務器通信就能獲得密鑰,也可以解密其他通信數據。所以相比非對稱加密,安全性較低,但是它的效率比非對稱加密高。
非對稱加密:非對稱加密會生成公鑰和私鑰,一般是服務端持有私鑰,公鑰向外公開。非對稱加密對內容單向加解密,即公鑰加密只能私鑰解,私鑰加密只能公鑰解。非對稱加密安全性雖然高,但是它的加解密效率很低。
CA證書:由權威機構頒發,用于驗證服務端的合法性,其內容包括頒發機構信息、公鑰、公司信息、域名等。
對稱加密不安全主要是因為密鑰容易泄露,那只要保證密鑰的安全,就可以得到兩全其美的方案,加解密效率高且安全性好。所以HTTPS在傳輸過程中,對內容使用對稱加密,而密鑰使用非對稱加密。
HTTP 緩存包括強緩存和協商緩存,強緩存的優先級高于協商緩存。緩存優點在于使用瀏覽器緩存,對于某些資源服務端不必重復發送,減小服務端的壓力,使用緩存的速度也會更快,從而提高用戶體驗。
強緩存在瀏覽器加載資源時,先從緩存中查找結果,如果不存在則向服務端發起請求。
HTTP/1.0 中可以使用響應頭部字段 Expires 來設置緩存時間。
客戶端第一次請求時,服務端會在響應頭部添加 Expirss 字段,瀏覽器在下一次發送請求時,會對比時間和Expirss的時間,沒有過期使用緩存,過期則發送請求。
HTTP/1.1 提出了 Cache-Control 響應頭部字段。
一般會設置 max-age 的值,表示該資源需要緩存多長時間。Cache-Control 的 max-age 優先級高于 Expires。
協商緩存的更新策略是不再指定緩存的有效時間,而是瀏覽器直接發送請求到服務端進行確認緩存是否更新,如果請求響應返回的 HTTP 狀態為 304,則表示緩存仍然有效。
Last-Modified 和 If-Modified-Since 對比資源最后修改時間來實現緩存。
ETag 和 If-None-Match 對比資源哈希值,哈希值由資源內容計算得出,即依賴資源內容實現緩存。
跨站腳本(Cross Site Scripting,XSS)指攻擊者在頁面插入惡意代碼,當其他用戶訪問時,瀏覽會器解析并執行這些代碼,達到竊取用戶身份、釣魚、傳播惡意代碼等行為。一般我們把 XSS 分為反射型、存儲型、DOM 型 3 種類型。
反射型 XSS 也叫“非持久型 XSS”,是指攻擊者將惡意代碼通過請求提交給服務端,服務端返回的內容,也帶上了這段 XSS 代碼,最后導致瀏覽器執行了這段惡意代碼。
反射型 XSS 攻擊方式需要誘導用戶點擊鏈接,攻擊者會偽裝該鏈接(例如短鏈接),當用戶點擊攻擊者的鏈接后,攻擊者便可以獲取用戶的 cookie 身份信息。
案例:
服務端直接輸出參數內容:
<? php
$input=$_GET["param"];
echo "<div>".$input."</div>";
惡意代碼鏈接:
http://www.a.com/test.php?param=<srcipt src="xss.js"></script>
存儲型 XSS 也叫“持久型XSS”,會把用戶輸入的數據存儲在服務端,這種XSS具有很強的穩定性。
案例:
比如攻擊者在一篇博客下留言,留言包含惡意代碼,提交到服務端后被存儲到數據庫。所有訪問該博客的用戶,在加載出這條留言時,會在他們的瀏覽器中執行這段惡意的代碼。
DOM 型 XSS 是一種特殊的反射型 XSS,它也是非持久型 XSS。相比于反射型 XSS,它不需要經過服務端,而是改變頁面 DOM 來達到攻擊。同樣,這種攻擊方式也需要誘導用戶點擊。
案例:
目標頁面:
<html>
<body>hello</body>
</html>
<script>
let search=new URLSearchParams(location.search)
document.write("hello, " + search.get('name') + '!')
</script>
惡意代碼鏈接:
http://www.a.com/test.index?name=<srcipt src="xss.js"></script>
CSRF 攻擊就是在受害者毫不知情的情況下以受害者名義偽造請求發送給受攻擊站點,從而在并未授權的情況下執行在權限保護之下的操作。CSRF 并不需要直接獲取用戶信息,只需要“借用”用戶的登錄信息相關操作即可,隱蔽性更強。
案例:
假設現在有一個博客網站,得知刪除博文的 URL 為:
http://blog.com?m=delete&id=123
攻擊者構造一個頁面,內容為:
<img src="http://blog.com?m=delete&id=123"></img>
攻擊者偽裝該網站鏈接并誘導用戶進行點擊,用戶恰好訪問過 blog.com,與該網站的 cookie 身份驗證信息還未過期。這時進入攻擊者的網站,img 發起請求,請求里攜帶上cookie,成功刪除博文。但是對于用戶是無感知的,當用戶返回到博客時會發現博文不見了,而這個請求是屬于合法請求,因為攻擊者借用受害者的身份信息進行操作。
攻擊者創建一個網頁利用 iframe 包含目標網站,然后通過設置透明度等方式隱藏目標網站,使用戶無法察覺目標網站的存在,并且把它遮罩在網頁上。在網頁中誘導用戶點擊特定的按鈕,而這個按鈕的位置和目標網站的某個按鈕重合,當用戶點擊網頁上的按鈕時,實際上是點擊目標網站的按鈕。
if (top.location !=location) {
top.location=self.location
}
轉自 https://www.cnblogs.com/chanwahfung/p/13616849.html
么理解H5與小程序這兩種不同的載體?這篇文章里,作者嘗試從產品經理角度對二者做了分析解讀,一起來看看,或許可以幫你更好地理清這兩個概念。
今天與大家聊聊H5和小程序。還記得小吳在剛成為產品經理時,經常會聽到技術同學甚至設計同學提到“這是個H5”、“用H5做就行了”之類的話,但那時的我并不理解H5是個什么東西,聽起來覺得很高大上,但實際上總覺得H5與優惠、活動、打折掛鉤,而且看上去千篇一律,辨識度很高。
H5和小程序是兩種不同的產品形態的載體,而且是當下比較流行的兩種技術解決方案。以下是小吳用文心一言4.0生成的對H5和小程序的本質與技術原理的解讀:
HTML5,通常稱為H5,是最新的HTML標準。它的核心優勢在于跨平臺性。無論是在桌面還是移動設備上,只要有瀏覽器,H5應用就可以運行。技術上,H5依賴于瀏覽器作為其運行環境,通過HTML、CSS和JavaScript等語言實現頁面布局、樣式設計和交互邏輯。
小程序,則是一種不需要下載安裝即可使用的應用,它實現了“觸手可及”的輕量級體驗。小程序通常依托于大型平臺(如微信、支付寶)的生態系統,運行在這些平臺的專門環境中。它們通過各自的開發框架,允許開發者快速構建應用。
相信大家看得一頭霧水,小吳用稍微簡單一點的表達方式再和大家解釋一下。
簡單來說,H5(HTML5)是一種制作網頁的標準,它就像是網頁的“建筑材料”,包含了文本、圖片、視頻和各種互動元素的排版和設計,也就是說H5就是一個網頁。
正常來說我們使用百度、谷歌等瀏覽器上網,訪問的都是一個個網頁,也就是說,只要你的設備上有瀏覽器,就可以訪問H5頁面。想象一下,如果每次要看一篇新聞或購物就需要先下載一個程序,是不是很麻煩?而且現在的APP想體驗服務都得注冊、登錄、輸入驗證碼,但H5就解決了這個問題,你直接在瀏覽器上打開網頁,無需下載安裝任何額外的應用,就可以享受其服務。
再來想像這樣一種場景,如果沒有H5,你想告訴你的朋友“年貨節開始啦,快去囤貨吧,打折力度真的好大”,恐怕你只能通過微信等其他聯系方式告知你的朋友,然后你的朋友還得下載京東或淘寶去享受年貨節。有了H5以后,京東等電商平臺都會將自己的促銷活動做成H5頁面的形式,這樣一來,你就可以將這個H5頁面分享給你的朋友,而你的朋友只需要點擊進去,就可以參與到這個活動當中。
所以H5頁面是可以通過鏈接分享的,這就像分享一個有趣的故事一樣簡單,用戶可以通過社交媒體、消息類應用輕松分享。
而小程序大家可以想象成一個迷你版的應用,它通常嵌入在像微信或支付寶這樣的大型應用中。使用小程序時,你不需要像普通應用那樣去應用商店下載,只需在微信或支付寶里搜索或掃碼就可以直接使用。比方說支付寶的螞蟻森林,微信的同城旅行買火車票,都是支付寶、微信生態下的小程序。
接下來我們來聊聊H5和小程序的區別,以及為什么有的業務選擇H5開發,而有些業務選擇基于小程序開發。
首先從運行環境的差異角度來說,我們經常從微信里點了一個鏈接分享,然后解析出來是一個商城的優惠活動,這基本上都是H5頁面。但我們應該沒在微信里面用過螞蟻森林或者其他支付寶系的小程序吧。這是因為它的核心優勢在于跨平臺性。無論是在桌面還是移動設備上,只要有瀏覽器,H5應用就可以運行。
而小程序則運行在特定平臺(如微信、支付寶)的專用容器中,這些容器為小程序提供了一套統一的API和界面標準。例如微信小程序的運行環境是微信應用本身,它為小程序提供了一系列微信特有的功能和接口,如微信支付、朋友圈等。這種專用環境確保了小程序在特定平臺上有更好的性能和用戶體驗,但同時也限制了它們在其他平臺上的運行。
從開發成本角度來說,H5相對來說較低一些。微信、支付寶這些互聯網巨頭都是移動互聯網時代的幸運兒,在移動互聯網時代之前,大多數朋友們都在通過電腦瀏覽器進行網上沖浪。而H5的開發依賴于傳統的Web技術棧,如HTML、CSS和JavaScript,這些技術廣泛應用且成熟,因此有大量現成的工具和框架可供使用,這些工具和框架可以顯著提升開發效率。
同時,H5項目通常只需要一套代碼即可在多個平臺上運行,這進一步減少了開發和維護的工作量。
小程序的開發則需要遵循特定平臺的開發框架和標準。例如微信小程序需要使用微信提供的開發工具和API,感興趣的各位可以搜索一下微信開放平臺。雖然這些平臺提供了一些便利的開發工具,但開發者仍需學習和適應每個平臺的特有規范和接口。此外,如果需要在多個平臺上推出小程序,開發者可能需要為每個平臺單獨開發和維護代碼,這無疑增加了工作量和成本。
雖然在開發成本方面H5要顯得比較有優勢,但是小程序的能力還是要更強大一些。如果我們把APP理解為程序,那小程序就是比程序小一點兒的程序而已,這變相地說明小程序是一種接近原生應用的技術。相較于H5,小程序能更深層次地訪問系統資源和權限。
例如小程序可以利用手機硬件(如攝像頭、GPS)、實現更豐富的功能(如掃一掃、即時支付)。這些深度整合的功能為小程序提供了更強大的能力,比如微信生態下的順豐快遞小程序,一打開就可以自動定位到我們的當前位置,此外我們也可以直接通過微信支付將快遞費轉給快遞小哥,甚至可以在小程序中直接打開我們的手機相機,進行快遞貨物的拍攝。
這些功能都是得益于小程序可以獲取我們手機的系統權限,通常我們在使用時都會收到手機的系統提示,比方說是否同意獲取相冊中全部照片,是否同意獲取當前位置等。雖然現代瀏覽器允許H5應用訪問一些設備功能,如地理位置、攝像頭等,但對于一些高級功能,如后臺運行、推送通知等,在H5應用中要么無法實現,要么實現起來較為復雜。
其實現在小程序還是很火爆的,無論是從產品開發的技術選型角度,還是從用戶體驗角度,小程序都完勝H5一籌,甚至比APP還要吃香。任何行業發展到中后期,都是幾家獨大,而群英薈萃百花齊放的場面通常是在行業初期。互聯網行業更是如此。
目前大家都知道的這幾家互聯網巨頭,都在強調所謂的生態概念。廣義來說,所謂的生態就是盡可能地覆蓋多的業務,盡可能地搶占市場以及用戶的時間,終極愿景是用戶手機里只需要安裝我的APP,就可以高度便捷化衣食住行,不再需要安裝其他APP了。
而從技術角度來理解生態,這么多的功能如何搭建在我的APP中呢?大家可以把這種生態模式理解為樂高積木,這些巨頭公司會把積木搭建方法提供給所有開發者并歡迎各位開發者來我的地盤上構建五花八門的樂高積木作品。但樂高的積木不能用在其他品牌的積木中。久而久之,樂高的積木越堆越大,這也便是生態的形成。
由于這些互聯網巨頭掌握著巨大的流量,很多互聯網公司已經將小程序業務作為主力業務,畢竟參天大樹好乘涼,寄居蟹也有流量吃。不知道在這個移動互聯網時代還會不會有新的生態出現,也不知道這種繁榮的生態下腐蝕的是不是互聯網從業者的創新能力。
最后希望這篇文章對各位有所幫助,祝各位睡個好覺。
作者:產品小吳,公眾號:產品小吳
本文由 @產品小吳 原創發布于人人都是產品經理,未經作者許可,禁止轉載。
題圖來自Unsplash,基于CC0協議。
該文觀點僅代表作者本人,人人都是產品經理平臺僅提供信息存儲空間服務。
讀:本文將從全國公路客運票務智慧管理系統的開發目的、開發背景、目標用戶、系統設計、系統架構、主要功能模塊等方面進行分析,軟件主要功能包括:班次管理、部門管理、車輛管理、車站管理、訂單管理、公告管理、角色管理、積分管理、路線管理、票務管理、評論管理、權限管理、日志管理、數據報表、投訴管理、用戶管理、優惠券管理、站內信管理、折扣管理,全文約5404字,需要10分鐘左右。感謝閱讀,如有建議和意見歡迎評論交流。
軟件開發目的與背景
全國公路客運票務智慧管理系統的設計初衷是為了徹底革新我國公路客運行業的服務質量和運營效率。隨著科技的飛速發展和數字化轉型的需求,傳統的票務管理方式已無法滿足日益增長的業務復雜性和用戶體驗的要求。本系統應運而生,旨在構建一個集智能化、集成化于一體的信息化平臺。
在過去的交通行業中,班次調度、部門協作、車輛維護等各個環節存在著信息孤島的問題,數據難以實時共享,不僅降低了工作效率,還可能導致服務失誤。我們的目標是通過這款系統,實現全程的信息化管理,消除信息壁壘,提升決策支持的精確度。
系統的關鍵模塊如班次管理、車輛管理、車站管理等,旨在優化線路規劃和資源調度,提高運輸效率;訂單管理和票務管理則致力于簡化購票流程,提供便捷的在線支付方式,提升乘客滿意度;角色管理和權限管理確保了數據安全和操作合規性。
此外,積分管理、優惠券管理、折扣管理等功能將激勵用戶重復使用服務,增強用戶粘性;投訴管理模塊能快速響應并解決用戶問題,體現我們對服務質量的高度關注;數據報表和日志管理為管理者提供了有力的數據分析工具,便于他們進行持續改進。
全國公路客運票務智慧管理系統的開發,不僅是技術進步的體現,更是推動我國公路客運行業向現代化、智能化轉型的重要一步。我們期待這款系統能助力整個行業步入全新的發展階段,實現服務升級、運營高效和用戶體驗的全面提升。
全國公路客運票務智慧管理系統是一款專為公路運輸行業的管理部門和運營商設計的高效信息化工具。它的主要應用場景是全國范圍內的長途汽車公司、客運站、旅行社等,這些單位需要管理和優化他們的運營服務,提升票務銷售效率,同時滿足乘客的便捷需求。
該系統的主要目標用戶是各級公路客運公司的管理層,如總經理、運營管理部、售票中心、客戶服務等部門負責人;售票員、客服人員、車輛駕駛員以及乘客。對于管理層,系統提供班次調度、部門協作、車輛維護、車站運營等全方位的管理平臺,幫助他們實時監控運營狀態,提高決策效率。售票員和客服人員可以通過訂單管理、票務查詢等功能,為乘客提供快速、準確的服務,同時也能通過積分管理、優惠券發放吸引和留住客戶。
對于乘客,系統則提供了在線購票、查詢班車時刻、路線選擇、評價反饋等一站式的服務,使得購票過程更為簡單快捷,同時還能通過站內信獲取最新資訊和優惠政策。投訴管理模塊能及時處理并解決乘客的問題,保障服務質量。
總而言之,全國公路客運票務智慧管理系統旨在通過智能化的技術手段,簡化業務流程,提升行業效率,優化用戶體驗,從而推動整個公路客運行業的數字化轉型和升級。
全國公路客運票務智慧管理系統是一款專為公路客運行業設計的全面信息化解決方案。該系統基于Java開發語言,采用了高效穩定的SpringMVC架構,數據庫則選用成熟可靠的MySQL,旨在提升行業運營效率和管理水平。
系統的核心功能模塊豐富多樣,涵蓋了業務流程的關鍵環節。班次管理模塊允許管理員靈活創建、修改和查詢各類班車信息,確保乘客查詢的準確性。部門、車輛和車站管理模塊實現資源的集中化管理,便于追蹤和維護。訂單管理模塊支持在線預訂、支付及實時狀態查詢,簡化購票流程。
公告管理、角色管理和權限管理模塊提供了一套完整的權限控制體系,確保信息安全與合規。積分管理鼓勵乘客積極參與,增強忠誠度。路線管理和票務管理模塊緊密結合,方便用戶了解線路信息和票務價格。評論管理模塊收集用戶反饋,提升服務質量。數據報表模塊提供詳盡的業務分析,幫助決策者做出明智選擇。
投訴管理模塊確保問題快速解決,維護用戶權益。用戶管理和優惠券管理模塊關注用戶體驗,優化營銷策略。站內信管理和折扣管理則進一步提升了用戶交互和促銷效果。日志管理確保系統的穩定運行,而數據安全通過嚴格的加密措施得以保障。
總之,全國公路客運票務智慧管理系統是一套全面、智能的管理工具,旨在通過數字化手段,提升公路客運行業的運營效率和服務質量,推動行業向現代化、智能化轉型。
全國公路客運票務智慧管理系統采用先進的B/S(Browser/Server)架構設計,這是一種基于互聯網的分布式應用模型,用戶通過瀏覽器訪問,服務器端負責數據處理和業務邏輯。以下是詳細的系統架構描述:
1. 前端界面:使用HTML5、CSS3及JavaScript技術構建,提供用戶友好的圖形化界面,包括各類模塊如班次查詢、訂單操作等,支持響應式設計,適應不同設備的訪問。
2. 后端服務:核心部分由Java語言開發,利用Spring MVC框架進行結構設計,實現了模塊間的松耦合。它負責處理用戶的請求,調用相應的業務邏輯,然后返回處理結果。
3. 數據庫管理:數據庫采用關系型數據庫,如MySQL或Oracle,存儲用戶信息、訂單記錄、車輛數據等關鍵信息,通過ORM工具如MyBatis進行數據交互。
4. 模塊化設計:系統分為多個獨立模塊,如票務管理、用戶管理等,每個模塊都有自己明確的功能,有利于團隊協作和代碼維護。
5. 權限與角色管理:通過Spring Security實現用戶權限控制,根據不同的角色賦予不同的操作權限,保證數據安全。
6. 日志與審計:系統集成日志管理模塊,記錄關鍵操作和異常情況,便于問題追蹤和審計。
7. 報表與數據分析:利用大數據處理技術,生成各類報表,幫助決策者分析運營狀況和優化策略。
8. 服務調用與接口:采用RESTful API設計,與其他系統和服務進行無縫對接,提高系統的靈活性和擴展性。
9. 安全措施:通過HTTPS協議保證數據傳輸安全,同時采用加密算法保護敏感信息,防止數據泄露。
總之,全國公路客運票務智慧管理系統通過這種高效的B/S架構設計,實現了高效、穩定、易用的運營管理,為公路客運行業提供了強大而智能的解決方案。
在瀏覽器中輸入系統網址,打開登錄界面后輸入登錄賬號、登錄密碼、驗證碼即可登錄。
工作臺包含:班次管理、部門管理、車輛管理、車站管理、訂單管理、公告管理、角色管理、積分管理、路線管理、票務管理、評論管理、權限管理、日志管理、數據報表、投訴管理、用戶管理、優惠券管理、站內信管理、折扣管理,根據不同角色權限菜單展示會有所區別。
管理功能主要字段信息包含:管理編碼、班次名稱、車輛型號、開車時間、到達時間、班次狀態等。使用表格形式展示數據信息,方便用戶查看和編輯。
管理設置新增、編輯、刪除、條件搜索、查看詳情等操作,可按照頁面提示進行操作執行,界面結構設計簡單,操作流程簡潔明了,可提升用戶操作體驗。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。