源 | https://www.jianshu.com/p/47bac9ccaaa7
在過去一年里,前端開發發展迅速,前端工程師的薪資亦是水漲船高。2020年也會熱度不減,而作為近年來尤為熱門的前端框架Vue.js 自是積累了大量關注。
本文將為你總結一下 2019 年里最值得關注的 45 個 Vue.js 開源項目——Let's go!
在過去的一年里,我們比較了將近 12000 個 Vue.js 開源項目和庫,并從中挑選了最好的 45 個(占比 0.37%)。
這些項目和庫可以分為 3 類:
這是一個極具權威性的表單,精確匯總了 2018 年 1-12 月期間發布的最佳 Vue.js 開源項目。
Mybridge 工作組從受歡迎程度、參與度和新鮮度三方面對它們進行了評估。
這些項目的 Github Star 平均數為 2620,可見其卓越不凡。
開源項目對程序員來說意義非凡,讓我們花些時間,一起來看看這些在去年可能被你錯過了的 Vue.js 開源項目吧。
1. Vuetify
2. Weex-ui
3. Eagle.js
4. Vuesax
5. Vue-ydui
6. Vue-grid-layout
7. Vue-virtual-scroller
8. Vue-content-loader
9. Mand-mobile
10. Vuikit
11. Vue-design-system v2.0
12. Vue-styleguidist
13. Heyui
14. Vue2-animate v2.0
15. Ui
16. Proppy
17. ZircleUI
18. Vue-overdrive
19. Vue-argon-design-system
20. Vue-cli
21. Vue-devtools(v 4.0)
22. Vue-native-core
23. Tiptap
24. Uni-app
25. Vue-rx v6.0
26. Eros
27. Vue-wait
28. Vue-starter
29. Vue-hooks
30. Portal-vue
31. Vue-fullpage.js
32. Vue-api-query
33. Vuese
34. Vuex-orm
35. Vuex-pathify
36. Vue-vr
37. Vuepress
38. Gridsome
39. Vue-music-webapp
40. Vue-realworld-example-app
41. Page-transitions-travelapp
42. Sample-vue-shop
43. Vuegg
44. Vue-filepond
Vue-filepond
45. Monimo
以上,就是我認為比較值得關注的 Vue.js 開源項目!
習的動力源于興趣,愿你在學習新知識時,動力源于興趣而并非其它
寫JQuery項目時,使用websocket很簡單,不用去考慮模塊化,組件之間的訪問問題,面向文檔編程即可,在Vue項目中使用時,遠遠沒有想象中的那么簡單,需要考慮很多場景,本篇文章將與各位開發者分享下vue-native-websocket庫的使用以及配置,用其實現群聊功能。先看下最終實現的效果
本文中對于vue-native-websocket庫的講解,項目中配置了vuex,對其不了解的開發者請移步官方文檔,如果選擇繼續閱讀本篇文章會比較吃力。
# yarn | npm 安裝
yarn add vue-native-websocket | npm install vue-native-websocket --save
復制代碼
import VueNativeSock from 'vue-native-websocket'
復制代碼
// main.js
// base.lkWebSocket為你服務端websocket地址
Vue.use(VueNativeSock,base.lkWebSocket,{
// 啟用Vuex集成,store的值為你的vuex
store: store,
// 數據發送/接收使用使用json格式
format: "json",
// 開啟自動重連
reconnection: true,
// 嘗試重連的次數
reconnectionAttempts: 5,
// 重連間隔時間
reconnectionDelay: 3000,
// 將數據進行序列化,由于啟用了json格式的數據傳輸這里需要進行重寫
passToStoreHandler: function (eventName, event) {
if (!eventName.startsWith('SOCKET_')) { return }
let method='commit';
let target=eventName.toUpperCase();
let msg=event;
if (this.format==='json' && event.data) {
msg=JSON.parse(event.data);
if (msg.mutation) {
target=[msg.namespace || '', msg.mutation].filter((e)=> !!e).join('/');
} else if (msg.action) {
method='dispatch';
target=[msg.namespace || '', msg.action].filter((e)=> !!e).join('/');
}
}
this.store[method](target, msg);
this.store.state.socket.message=msg;
}
});
復制代碼
// vuex配置文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
token:"",
userID:"",
// 用戶頭像
profilePicture: "",
socket: {
// 連接狀態
isConnected: false,
// 消息內容
message: '',
// 重新連接錯誤
reconnectError: false
}
},
mutations: {
SOCKET_ONOPEN (state, event) {
// 連接打開觸發的函數
Vue.prototype.$socket=event.currentTarget;
state.socket.isConnected=true
},
SOCKET_ONCLOSE (state, event) {
// 連接關閉觸發的函數
state.socket.isConnected=false;
console.log(event);
},
SOCKET_ONERROR (state, event) {
// 連接發生錯誤觸發的函數
console.error(state, event)
},
SOCKET_ONMESSAGE (state, message) {
// 收到消息時觸發的函數
state.socket.message=message
},
SOCKET_RECONNECT(state, count) {
// 重新連接觸發的函數
console.info(state, count)
},
SOCKET_RECONNECT_ERROR(state) {
// 重新連接失敗觸發的函數
state.socket.reconnectError=true;
},
},
actions: {
customerAdded (context) {
// 新連接添加函數
console.log('action received: customerAdded');
console.log(context)
}
},
modules: {
}
})
復制代碼
至此vue-native-websocket配置結束,如需了解更多配置方法,請移步npm倉庫
// 監聽消息接收
this.$options.sockets.onmessage=(res)=>{
// res.data為服務端返回的數據
const data=JSON.parse(res.data);
// 200為服務端連接建立成功時返回的狀態碼(此處根據真實后端返回值進行相應的修改)
if(data.code===200){
// 連接建立成功
console.log(data.msg);
}else{
// 獲取服務端推送的消息
const msgObj={
msg: data.msg,
avatarSrc: data.avatarSrc,
userID: data.userID
};
// 渲染頁面:如果msgArray存在則轉json
if(lodash.isEmpty(localStorage.getItem("msgArray"))){
this.renderPage([],msgObj,0);
}else{
this.renderPage(JSON.parse(localStorage.getItem("msgArray")),msgObj,0);
}
}
};
復制代碼
// 消息發送函數
sendMessage: function (event) {
if (event.keyCode===13) {
// 阻止編輯框默認生成div事件
event.preventDefault();
let msgText="";
// 獲取輸入框下的所有子元素
let allNodes=event.target.childNodes;
for(let item of allNodes){
// 判斷當前元素是否為img元素
if(item.nodeName==="IMG"){
msgText +=`/${item.alt}/`;
}
else{
// 獲取text節點的值
if(item.nodeValue!==null){
msgText +=item.nodeValue;
}
}
}
// 消息發送: 消息內容、狀態碼、當前登錄用戶的頭像地址、用戶id
this.$socket.sendObj({msg: msgText,code: 0,avatarSrc: this.$store.state.profilePicture,userID: this.$store.state.userID});
// 清空輸入框中的內容
event.target.innerHTML="";
}
}
復制代碼
// 渲染頁面函數
renderPage: function(msgArray,msgObj,status){
if(status===1){
// 頁面第一次加載,如果本地存儲中有數據則渲染至頁面
let msgArray=[];
if(localStorage.getItem("msgArray")!==null){
msgArray=JSON.parse(localStorage.getItem("msgArray"));
for (let i=0; i<msgArray.length;i++){
const thisSenderMessageObj={
"msgText": msgArray[i].msg,
"msgId": i,
"avatarSrc": msgArray[i].avatarSrc,
"userID": msgArray[i].userID
};
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
}
}else{
// 判斷本地存儲中是否有數據
if(localStorage.getItem("msgArray")===null){
// 新增記錄
msgArray.push(msgObj);
localStorage.setItem("msgArray",JSON.stringify(msgArray));
for (let i=0; i <msgArray.length; i++){
const thisSenderMessageObj={
"msgText": msgArray[i].msg,
"msgId": i,
"avatarSrc": msgArray[i].avatarSrc,
"userID": msgArray[i].userID,
};
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
}else{
// 更新記錄
msgArray=JSON.parse(localStorage.getItem("msgArray"));
msgArray.push(msgObj);
localStorage.setItem("msgArray",JSON.stringify(msgArray));
const thisSenderMessageObj={
"msgText": msgObj.msg,
"msgId": Date.now(),
"avatarSrc": msgObj.avatarSrc,
"userID": msgObj.userID
};
// 解析并渲染
this.messageParsing(thisSenderMessageObj);
}
}
}
復制代碼
// 消息解析
messageParsing: function(msgObj){
// 解析接口返回的數據進行渲染
let separateReg=/(\/[^/]+\/)/g;
let msgText=msgObj.msgText;
let finalMsgText="";
// 將符合條件的字符串放到數組里
const resultArray=msgText.match(separateReg);
if(resultArray!==null){
for (let item of resultArray){
// 刪除字符串中的/符號
item=item.replace(/\//g,"");
for (let emojiItem of this.emojiList){
// 判斷捕獲到的字符串與配置文件中的字符串是否相同
if(emojiItem.info===item){
const imgSrc=require(`../assets/img/emoji/${emojiItem.hover}`);
const imgTag=`<img src="${imgSrc}" width="28" height="28" alt="${item}">`;
// 替換匹配的字符串為img標簽:全局替換
msgText=msgText.replace(new RegExp(`/${item}/`,'g'),imgTag);
}
}
}
finalMsgText=msgText;
}else{
finalMsgText=msgText;
}
msgObj.msgText=finalMsgText;
// 渲染頁面
this.senderMessageList.push(msgObj);
// 修改滾動條位置
this.$nextTick(function () {
this.$refs.messagesContainer.scrollTop=this.$refs.messagesContainer.scrollHeight;
});
}
復制代碼
通過每條消息的userID和vuex中的存儲的當前用戶的userID來判斷當前消息是否為對方發送
<!--消息顯示-->
<div class="messages-panel" ref="messagesContainer">
<div class="row-panel" v-for="item in senderMessageList" :key="item.msgId">
<!--發送者消息樣式-->
<div class="sender-panel" v-if="item.userID===userID">
<!--消息-->
<div class="msg-body">
<!--消息尾巴-->
<div class="tail-panel">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zbds30duihuakuangyou"></use>
</svg>
</div>
<!--消息內容-->
<p v-html="item.msgText"/>
</div>
<!--頭像-->
<div class="avatar-panel">
<img :src="item.avatarSrc" alt="">
</div>
</div>
<!--對方消息樣式-->
<div class="otherSide-panel" v-else>
<!--頭像-->
<div class="avatar-panel">
<img :src="item.avatarSrc" alt="">
</div>
<!--消息-->
<div class="msg-body">
<!--消息尾巴-->
<div class="tail-panel">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zbds30duihuakuangzuo"></use>
</svg>
</div>
<!--消息內容-->
<p v-html="item.msgText"/>
</div>
</div>
</div>
</div>
復制代碼
最后更新時間: 2020年2月1日
// main.js 在插件配置里添加connectManually屬性
// 開啟手動調用 connect() 連接服務器
connectManually: true
復制代碼
// 使用this.$connect(URL)方法
this.$connect(`${base.lkWebSocket}/${localStorage.getItem("userID")}`);
復制代碼
// beforeDestroy生命周期中調用$disconnect方法
beforeDestroy() {
// 頁面銷毀時,斷開連接
console.log("頁面銷毀,斷開websocket連接");
this.$disconnect();
},
復制代碼
// 開啟json傳輸時使用sendObj進行消息發送
this.$socket.sendObj({
});
// 為開啟json傳輸時,使用send()函數進行發送
this.$socket.send("");
復制代碼
// src/store/index.js
state{
socket{
// 心跳消息發送時間
heartBeatInterval: 30000,
// 心跳定時器
heartBeatTimer: 0
}
}
mutations:{
// 連接打開
SOCKET_ONOPEN (state, event) {
Vue.prototype.$socket=event.currentTarget;
state.socket.isConnected=true;
// 連接成功時啟動定時發送心跳消息,避免被服務器斷開連接
state.socket.heartBeatTimer=setInterval(()=> {
const message="心跳消息";
state.socket.isConnected && Vue.prototype.$socket.sendObj({"code":200,"msg":message});
}, state.socket.heartBeatInterval);
},
// 連接關閉
SOCKET_ONCLOSE (state, event) {
state.socket.isConnected=false;
// 連接關閉時停掉心跳消息
clearInterval(state.socket.heartBeatTimer);
state.socket.heartBeatTimer=0;
console.log('連接已斷開: ' + new Date());
console.log(event);
},
}
復制代碼
品 | 51CTO技術棧(微信號:blog51cto)
最近,網傳谷歌正考慮將其AI搜索最新成果置于付費墻之后。
作為全球搜索引擎市場的絕對霸主,谷歌的一舉一動牽連甚廣。眾所周知,搜索領域的生態格局已多年未有劇變,自去年開始,大模型的流行、生成式AI的崛起卻攪起了“一池春水”。
這一背景下,“領頭羊”谷歌也不得不調整其在搜索方面的布局。如今,谷歌的新動作雖然還在醞釀階段,但其透露出的信號已經讓一些人開始質疑。
據外媒《金融時報》報道,谷歌正在內部進行討論,是否將部分AI搜索功能轉移到高級訂閱服務之中。在媒體向谷歌發言人求證時,對方并未否認這一消息,但也沒有透露任何細節。
事實上,與多數人預想中不同,谷歌在AI搜索問題上處于兩難境地。
盡管微軟在Bing搜索引擎中添加生成式AI功能并未顯著改變市場份額,但要是在搜索查詢中使用生成式AI技術,則有可能改變谷歌現有的以廣告為主要收入的商業模式。
去年,谷歌推出了搜索生成體驗(SGE)。與微軟的必應將ChatGPT制作成“聊天”界面不同,SGE將搜索和生成式AI相結合,用戶在搜索時可以獲得一份單獨的摘要“快照”答案和驗證答案的網站鏈接。同時,谷歌也會明確標記這些回答為“Generative AI is experimental”,并與自然搜索結果進行區分。
當然,在AI生成的結果旁邊也會插入廣告。可是,從傳統的搜索結果列表轉變為這種新形式,不得不讓人考慮,這是否會改變用戶與廣告互動的方式,進而影響到谷歌的收入結構。
盡管SGE尚未對谷歌的核心搜索引擎業務產生重大影響,但撇開任何潛在的對公司廣告收入的影響不談,由于AI技術對于資源的高度依賴性,將其推廣給更多用戶也可能帶來高昂的成本。因此,SGE成為了一個極有可能被設置為付費墻后的高級功能。
同時,谷歌發言人也明確表示,他們會積極開發更多與AI相關的增值服務,但他們并沒有計劃推出無廣告的搜索產品。
這一消息傳出后,全球最大的高性能計算(HPC)解決方案提供商之一、德國企業Northern Data Group的首席運營官Rosanne Kincaid-Smith表示了不滿。在她看來:
第一,谷歌所處的位置獨特。它與OpenAI等專注于開發更多新事物的科技公司不同,谷歌擁有龐大的影響力和覆蓋范圍,且在搜索引擎市場占據主導地位,應當將AI搜索創新成果公之于眾,“允許大眾接觸并使用AI所帶來的變革性技術”。
第二,將AI搜索創新成果置于付費墻背后討論的本質,其實涉及到AI倫理和數據治理問題。“我們所有在這個領域的人……都負有直接的責任,要確保AI的道德管理是他們提供給公眾的服務包不可或缺的一部分,而不是需要額外付費的內容。”
先不論這次事件孰是孰非,通過觀察谷歌的一系列動作可以發現,谷歌實質上是在努力尋找讓人工智能與廣告業務共生的方式。不能改變主要的盈利模式,但也不得不在功能上繼續加碼AI搜索。
“AI+搜索”從來不是一個新鮮議題。但是這一趨勢從未像當下一樣不可逆轉。
隨著搜索的AI化進程不斷推進,AI搜索成為了全球商業市場上又一“兵家必爭之地”,新舊勢力紛紛亮劍,無論是想要做蛋糕的還是分蛋糕的,都在努力解鎖新場景,尋找真正的殺手锏。
國外,微軟一套組合拳強勢出擊,加持了GPT-4的Bing快速掀起風暴;被刺激到的谷歌隨后推出SGE服務,將生成式AI功能整合到搜索中;初創公司Perplexity AI和Glean則分別立足C端和B端市場,在短期內成長為獨角獸,實力不容小覷。
國內,同樣是群雄林立。百度依托文心一言將旗下傳統搜索升級為AI互動式搜索引擎;昆侖萬維的天工AI搜索,360集團的360 AI搜索等產品也相繼涌現;此外,淘寶啟動了“淘寶問問”,抖音則在APP內測試“AI搜”,這波時潮為短視頻、電商等領域的玩家提供了新解。
AI搜索賽道看起來競爭已趨白熱化,但如果從更大的邊際來看,在短期內,AI搜索不可能顛覆現有的搜索生態。
圖片
截圖來自StatCounter:2023年3月至2024年3月,全球搜索引擎市場份額基本沒有太多變化,谷歌依舊一騎絕塵
首先,基于AI所做的創新有限。迄今而至,并沒有出現一款真正的殺手級搜索產品。大廠在進行搜索革新時相對謹慎,大部分成熟的搜索產品并沒有太多令人驚艷的突破。
再者,AI搜索的正確性有待提高。“真實可靠”是用戶對搜索引擎的核心訴求,但在AI生成的產物在互聯網上泛濫時,如何保障用戶看到可信的搜索結果,仍舊是諸多企業有待解決的問題。
最后,用戶慣性阻力巨大。相比AI搜索,傳統搜索多年來已經根植于多數人的工作和生活中,路徑依賴下人們還沒有足夠的動力從傳統的搜索方式中轉向。
曾在短期內迅速吸引了大量用戶的小眾搜索引擎Neeva僅僅維持了四年就停止運營,最終被Snowflake收購。
其創始人在宣布Neeva關閉的博客文章中寫道:“在整個旅程中,我們發現構建搜索引擎是一回事,說服普通用戶轉向更好的選擇則完全是另一回事。”
順著Neeva的故事,其實我們可以清晰看到圍繞搜索引擎構建的兩條完全不同的發展之路。
路線1:谷歌模式
在線廣告至今仍是谷歌最主要的收入來源。其中,搜索廣告業務的地位至關重要。
根據Alphabet發布的2023財年第四季度財報,谷歌第四季度廣告營收為655.17億美元,同比增長11%;其中,核心搜索業務營收480.2億美元,同比增長13%。
路線2:反谷歌模式
一些新興的AI搜索企業更傾向于回歸搜索本身的價值,讓用戶看到他們喜歡的內容,而不是更有“商業價值”的內容。
在產品形式上,AI搜索最重要的一點升級在于:用戶終于不用在滿屏廣告中尋找答案了。這種“反谷歌”的搜索方式,決定了其主要收入來源不會是廣告。
比如Neeva,其成立的初衷就在于其創始人Ramaswamy認為:從長遠來看,以廣告為基礎的模式必然導致搜索結果的劣化。要想打造更好的搜索引擎,首先需要改變激勵措施。
無獨有偶,新興搜索引擎Perplexity.ai、You.com同樣依賴訂閱來實現盈利,讓用戶為搜索服務本身付費。
在中國的一眾搜索創業者中,有中國版Perplexity之稱的秘塔AI搜索同樣以“沒有廣告,直達結果”的slogan聲名鵲起。
截圖來自秘塔AI官網
據similarweb的統計數據顯示,截至3月1日前28天,秘塔AI搜索網站的訪問量高達478.3萬次。不過目前尚不清楚,秘塔AI未來將采用什么樣的商業模式。
綜合來看兩種路線,就發展現狀來說,前者的優勢幾乎是碾壓性的。曾一度被視為創業標桿的Perplexity如今也有了轉舵的意向。
本來除了基礎的免費版本,Perplexity還提供更為強大的Pro訂閱服務。但這種模式并不足以支撐其發展。Perplexity 剛剛宣布,它正在將廣告納入其搜索結果中。
截圖來自Perplexity官網
與原來的無廣告模式相比,這種新模式顯然為公司開辟了更多的收入渠道。分析人士認為,Perplexity決定在其平臺中整合廣告并非僅僅出于盈利考慮,更是一種維持并提升其服務的戰略舉措。
“投放廣告能夠為其提供持續改進平臺、保持競爭優勢所需的資源。然而,這一新特性引發了更多的問題,比如,如何在不損害用戶所期待的質量與完整性前提下,將廣告無縫融入搜索結果。”
AI作為變數介入搜索領域已是既定事實。
我們姑且將谷歌的新動作視為其尋求AI搜索商業化之路的投石問路之舉。基于其互動形式與廣告業務的天然矛盾,在沒有找到更合適的盈利模式以前,谷歌必須踩著鋼絲,讓兩者盡可能和諧共存。
而對于新興的搜索初創企業,AI搜索成為了一個契機,讓搜索真正回歸其本色:幫助人們找到自己想要的頁面,并避開途中的一切障礙。但是如何真正在固有格局中存活下來,實現可持續發展依然是首要問題。同時,屠龍少年是否會終成惡龍,也需要警惕。
對于新技術浪潮帶來的影響,人們往往會高估未來一年的變化,而低估未來五年乃至十年的變化。新興搜索廠商如何在傳統市場殺出血路?真正的爆款AI搜索產品會是何種模樣?全球搜索生態是否會重新洗牌?這些未解之謎,終將有時間給出答案。
https://www.theregister.com/2024/04/08/google_search_paywall
https://www.thepaper.cn/newsDetail_forward_24783786
https://www.woshipm.com/ai/6009702.html
https://www.singlegrain.com/blog/n/perplexity-ads/
https://www.duidaima.com/Group/Topic/IT/14562
想了解更多AIGC的內容,請訪問:
51CTO AI.x社區
https://www.51cto.com/aigc/
來源: 51CTO技術棧
*請認真填寫需求信息,我們會在24小時內與您取得聯系。