vue開發的項目,前端寫的.vue文件中的生命周期方法,線上還存在嗎?
瀏覽器本身是不能識別.vue後綴的文件的是吧,我默認它只識別.html先,我又知道,vue是一個純前端的開發框架,那麼我們本地寫好的vue項目,在執行npm run build之後,全部打包到dist文件夾當中,然後將dist文件夾中的內容放到伺服器上,本地用瀏覽器訪問伺服器時候,是如何使得展現在瀏覽器中的頁面的邏輯跟開發時候的一致的? 比如我們開發時候在mounted方法中寫道 頁面mounted之後就alert(『頁面mounted了』),本地開發調試好,打包到dist 上傳到伺服器,用瀏覽器訪問,瀏覽器中成功alert了,為什麼?? 瀏覽器不認識mounted這個方法的吧,它怎麼知道這行代碼在什麼時間執行??
文檔和源碼是人類最好的老師。——魯迅
瀉藥。
隨著前端工程化、自動化的深入,代碼一步步的被推進了黑盒子。也許幾年之後,前端代碼是怎樣運行的會成為另一門「計算機系統」課程吧。
個人覺得題主的問題可以分成兩部分:
- .vue文件是如何被編譯的
- vue代碼是如何運行的
從.vue到.js
你在開發時,通常會執行下面的代碼:
npm run dev
node中的npm模塊會在package.json中尋找dev對應的script:
{
...
"scripts": {
...
"dev": "NODE_ENV=develop node build/dev-server.js"
}
}
執行script,通過webpack編譯.vue,並開啟了一個本地伺服器,主要的編譯配置如下:
module.exports = {
entry: {
// 編譯入口,會從這裡索引依賴樹
},
output: {
// 編譯結果輸出在哪裡
},
module: {
// 經過了哪些loader處理
rules: [
{
test: /.vue$/,
loader: "vue-loader",
}
]
}
}
vue-loader會通過正則匹配import進來的vue文件:
import Hello from "HelloComponent.vue" // .vue可以省略
vue-loader會解析.vue文件中的代碼塊,默認有三塊:
&
...
&
&
&
我們用vue-loader暴露的介面模擬一下這個過程:
在options里添加需要解析的代碼塊
const scriptLoader = require.resolve("./loaders/my-script-loader.js")
module.exports = {
entry: {
// 編譯入口,會從這裡索引依賴樹
},
output: {
// 編譯結果輸出在哪裡
},
module: {
// 經過了哪些loader處理
rules: [
{
test: /.vue$/,
loader: "vue-loader",
options: {
loaders: {
// 匹配&
"myScript": scriptLoader
}
}
}
]
}
}
定義一個my-script-loader.js,用於處理這個代碼塊:
module.exports = function (source, map) {
// source是處理結果,map是映射關係
this.callback(null, "myscript is JSON.stringify(source)", map)
// this.callback 用於返回這個loader的處理結果
}
這個loader為原來的代碼塊添加了幾個字元"myscript is",vue-loader對script做了什麼呢:
// 你的代碼
export default {
data: {
hello: "hello world"
},
mounted: function () {
console.log(this.hello)
}
}
// 編譯後(還沒經過babel和uglify)的代碼
new Vue({
data: {
hello: "hello world"
},
mounted: function () {
console.log(this.hello)
}
})
沒錯,生成了一個組件,當然實際情況要比這複雜的多。
這裡涉及到的文檔有:vue-loader官方文檔和webpack官方文檔
vue的生命周期
好了,現在.vue已經被成功的編譯成了html、css和js代碼,可以放在瀏覽器運行了。你應該會問,這個mounted是從哪裡冒出來的?
還記得你在main.js文件里的第一行代碼嗎
import Vue from "vue"
這行代碼將node_modules/vue/dist中的文件引入了,當你查看源碼的時候,你就會發現當你new Vue()的時候發生了什麼。
在這裡定義了vue的構造函數:
import { initMixin } from "./init"
import { stateMixin } from "./state"
import { renderMixin } from "./render"
import { eventsMixin } from "./events"
import { lifecycleMixin } from "./lifecycle"
import { warn } from "../util/index"
function Vue (options) {
if (process.env.NODE_ENV !== "production"
!(this instanceof Vue)
) {
warn("Vue is a constructor and should be called with the `new` keyword")
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
// 這裡是生命周期相關部分
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
在這裡定義了生命周期函數,初始化了很多東西並在最後調用了你定義的mounted:
export function lifecycleMixin (Vue: Class&
...
// we set this to vm._watcher inside the watcher"s constructor
// since the watcher"s initial patch may call $forceUpdate (e.g. inside child
// component"s mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, "mounted")
}
return vm
}
至此,你定義在mounted里的代碼已經在瀏覽器執行了。
結尾
以前總感覺前端的門檻低,後來感覺前端上升難。最後終於在刷文檔和源碼中找到了一條小路。加油~共勉咯~,FEer~
以上。
所謂生命周期方法,就是一個回調函數,它不需要被瀏覽器『認識』,只要在恰當的時候被觸發就好。
本地用瀏覽器訪問伺服器時候,是如何使得展現在瀏覽器中的頁面的邏輯跟開發時候的一致的?
這部分工作就是執行npm run build 之後做的事情,vue-loader會幫你把你寫的.vue 文件轉變成原生 js,當然這個轉變過程也不會破壞原有的業務邏輯。
vue 還有一種用法,可以直接引入vue.js 文件到前端頁面,然後同樣去寫mounted 方法,你可以追蹤調試一下執行過程,其實是一樣的。
你可以認為是 vue.js 認識 mounted 這個方法,它會在恰當的時候調用這個函數。
這就是一年前前端圈吵的天翻地覆的 "不紮實基礎直接上框架的" 典範啊
我在 react 社區也看到不少基礎沒打好就上框架的 回答一下問題吧,三大框架無論哪個都好,編譯後都不過是純js語法,你看一下編譯後的代碼就不會認為瀏覽器無法執行了
好大的誤區。。。
vue組件不是面向瀏覽器開發的,而是面向vue這個框架開發的,組件的所有內部語法都是vue提供的,只要vue能載入並理解組件就行了,可以簡單的理解成jquery和jquery插件的關係。
另外vue後綴的組件文件在構建過程中會被編譯成普通的js代碼,類似在vue實例中局部註冊組件,線上運行的只有js而已。@誒嘿~好像被邀請了。
其實想搞明白這件事情,超簡單噠。
我們知道,瀏覽器不能識別的.vue文件之所以能被載入,是因為有webpack的原因,webpack之所以可以載入.vue文件,是因為有vue-loader的原因。
而vue-loader是webpack的loader,那麼它就肯定是一個輸入一個字元串,輸出一個字元串的函數嘛。
辣么就好說了,用vue-cli創建一個超級無敵簡單的項目。
npm install -g vue-cli
vue init webpack-simple hello-vue
cd hello-vue
npm install
npm run dev # ready to go!
然後發現項目裡面有一個App.vue文件,內容大概是這樣:
&
&
&
&{{ msg }}&
&Essential Links&
&
&
&Ecosystem&
&
&
&
&
&
然後在node_modules裡面vue-loader的源碼里加一行console.log,看看它輸出了啥玩意兒……
var disposed = false
function injectStyle(ssrContext) {
if (disposed) return
require("!!vue-style-loader!css-loader?sourceMap!../node_modules/vue-loader/lib/style-compiler/index?{"vue":true,"id":"data-v-04c2046b","scoped":false,"hasInlineConfig":false}!../node_modules/vue-loader/lib/selector?type=stylesindex=0bustCache!./App.vue")
}
var normalizeComponent = require("!../node_modules/vue-loader/lib/component-normalizer")
/* script */
import __vue_script__ from "!!babel-loader!../node_modules/vue-loader/lib/selector?type=scriptindex=0bustCache!./App.vue"
/* template */
import __vue_template__ from "!!../node_modules/vue-loader/lib/template-compiler/index?{"id":"data-v-04c2046b","hasScoped":false,"buble":{"transforms":{}}}!../node_modules/vue-loader/lib/selector?type=templateindex=0bustCache!./App.vue"
/* template functional */
var __vue_template_functional__ = false
/* styles */
var __vue_styles__ = injectStyle
/* scopeId */
var __vue_scopeId__ = null
/* moduleIdentifier (server only) */
var __vue_module_identifier__ = null
var Component = normalizeComponent(
__vue_script__,
__vue_template__,
__vue_template_functional__,
__vue_styles__,
__vue_scopeId__,
__vue_module_identifier__
)
Component.options.__file = "src\App.vue"
if (Component.esModule Object.keys(Component.esModule).some(function (key) {
return key !== "default" key.substr(0, 2) !== "__"
})) {
console.error("named exports are not supported in *.vue files.")
}
/* hot reload */
if (module.hot) {
(function () {
var hotAPI = require("vue-hot-reload-api")
hotAPI.install(require("vue"), false)
if (!hotAPI.compatible) return
module.hot.accept()
if (!module.hot.data) {
hotAPI.createRecord("data-v-04c2046b", Component.options)
} else {
hotAPI.reload("data-v-04c2046b", Component.options)
" + "
}
module.hot.dispose(function (data) {
disposed = true
})
})()
}
那麼如何證明我剛才說的那個猜想「大概是對的」呢?好簡單噠,打開chrome控制台,去看源碼那個面板……有驚喜哦。
透過現象看本質,js就是本質,vue文件最終是要被編譯成js文件的,瀏覽器能夠識別和運行的也是js文件。
話說現在的前端已經是這樣子的了嗎?直接跳過js來玩vue?除非你上 babel-loader 之類的,你 npm build 基本上把 vue 轉成了 bundle.js 然後 publish 一個 引用了 bundle.js 的 頁面
如果是問 debug 怎麼找到 vue 的,那麼靠的是 sourcemap多看看官網上的說明,配合那張生命周期的圖很好理解的
另外,想起之前大漠說的"會vue的小白"…看了你在其他回答下的評論,我覺得你要的答案是:vue這個翻譯官也被打包進dist文件了,被翻譯的是其他被打包進來的vue實例。用cdn的話就是從cdn叫來了一位vue翻譯官。
翻譯官還分很多類型哦,包括會翻譯template和不會翻譯template的,你可以去了解了解每一位翻譯官都適用於什麼環境和場景。謝邀。
題主一共有兩個問題,我分別解答吧。
1. 瀏覽器是怎麼執行我們寫的 .vue 文件中的代碼的?
你駕駛一架飛機由於燃料耗盡被迫降落在科隆島,為了回家,你必須跟當地人交流,獲取燃料,但當地人使用的是一種叫做迦艾斯的語言。幸運的是尤大大在科隆島部署了一種裝置,能夠自動將你說的話自動翻譯成迦艾斯語。於是你還是順利的向居民要到了燃料。儘管你所使用的語言,科隆島上的居民是不懂的。實際上不論你是 npm run build 還是 npm run dev ,vue.js 框架提供的工具都已經將 .vue 文件轉化成了 js ,而這一切對你來說都是透明的。至於翻譯的準不準確,那就取決於自動翻譯裝置是否出bug了,如果翻譯工具有bug,你要燃料,然後當地居民直接一刀把你剁了也不是沒有可能。。。
2. 為什麼瀏覽器不認識 mounted 卻知道 mounted 在什麼時候執行呢?
我們寫一個簡單的例子來看這個問題,我們來實現一個框架,讓瀏覽器在1秒後執行oneSecondLater方法。那麼這樣一個框架語需要怎麼實現呢,其實超簡單。
oneSecondLater.js :
setTimeout("oneSecondLater()", 1000);
只要在你的代碼之前引入 oneSecondLater.js 然後你就可以這麼寫了:
function oneSecondLater(){
console.log("Hello Future");
}
所以瀏覽器知道 oneSecondLater 是1秒之後執行,而不是2秒,是因為之前我們事先告訴過它了。
最佩服那些評論基礎不行還不給出答案的人,感情是自己也不知道吧?國內前端水平都這麼高了 源碼都成了初級程序員的基礎了
有的人都扯到源碼那麼遠了, 我就說題主難道不知道vue可以用script標籤方式向jq一樣引用使用么. 如果題主這樣用會了怎會提出如此問題
簡單說就是 npm run build 把瀏覽器不認識的 .vue 編譯成了瀏覽器認識的 .js
先從基礎開始的就業困難,從框架開始的各種懵逼。真tm有意思。
.vue文件可以是任意一個單詞,只要裡面的內容能被webpack讀取出來,並生成瀏覽器看得懂html,js,css文件就行。
超級省略的說一下
- vue.js
function Vue (options) {
this.create()
options.created()
this.mount()
options.mounted()
return this
}
// 生命周期的機制差不多就是這樣
你寫的
export default {
data: {xxx:xxx},
mounted () {}
}
編譯完
function Vue (options) {
this.create()
options.created()
this.mount()
options.mounted()
return this
}
new Vue({
data: {xxx:xxx},
mounted () {}
})
// 當然實際編譯完不長這樣,我只想說它會帶著vue的代碼或者機制打包進來。
你說什麼時候調用mounted是瀏覽器的事兒么?是vue的控制的,瀏覽器只管執行。
瀏覽器除了原生的方法,啥都不認識。
吃棗要玩啊。。。
可能你需要知道webpack是幹什麼的可能你需要了解vue-loader做了什麼往下走可能是vue源碼,多看文檔,多Google,少刷知乎多看書
按你這麼說的話
尤哥這是創造了一種新規範啊!比React還厲害直接插手W3C!你在npm run dev的時候瀏覽器也不是執行的vue,也是執行的編譯過後的js。npm run build的時候也是一樣。區別是兩者編譯的結果根據不同的場景會有所不同
我之前也有類似的困擾。看了相關文檔之後解決大部分問題https://vue-loader.vuejs.org/zh-cn/這裡寫了篇使用vue-cli 執行npm run dev後都發生的事情。https://mafei.fun/2017/09/23/vue-webpack-boilerplate/
首先,即使不使用「構建」,而是使用 script 標籤引入 Vue.js,瀏覽器也不認識 mounted,幸運的是,Vue.js 認識 mounted,在使用 new Vue 創建 Vue 實例的時候,Vue 已經對實例(組件)選項進行編譯,其中的鉤子函數會在 Vue 的生命周期中起作用。
既然你有這個疑問,你需要學習 webpack,以及 vue-loader,就像學習《葵花寶典》的前提是「XXXX」一樣。網上有很多相關的內容,如果願意,可以到SegmentFault找到相應的講座看看,或者去極客學院學習相應的課程。
不過 vue-cli 工具及其項目模板是開箱即用的,因此「如不自宮,也能成功」。就醬
推薦閱讀:
※jQuery的ajaxSubmit如何實現批量圖片非同步上傳?
※除了Bootstrap,有沒有更好的響應式框架用來開發外包項目?
※2017前端技術規劃該包含什麼?
※mongodb與mysql的應用場景?