跳到主要内容

Vue 的引用

这里我们列举两种常用的方法

联合作者

cxxjackie/李恒道/andywang425

创建标签式引用

完整代码及分析如下

// ==UserScript==
// @name 测试Vue引用
// @namespace http://tampermonkey.net/
// @version 0.1
// @author LHD
// @match https://bbs.tampermonkey.net.cn/*
// @grant none
// @run-at document-start
// ==/UserScript==

// 初始化一个有地址的 sciprt 标签,并且注入到页面中
// 注意这里我们使用了 run-at 让其在页面开始尽早注入脚本。
let script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = "https://cdn.jsdelivr.net/npm/vue@next";
document.documentElement.appendChild(script);

// 添加一个window.onload回调
window.onload = () => {
// text是其我们想插入的文本
// 注意:id="app"是为了让我们可以方便的找到元素
// {{ message }}是属于vue的语法糖
// 只要在{{}}输入其变量或表达式名字,就会在运行时填入变量并返回其结果
let text = `<div id="app" style="position: absolute;top: 50vh;left: 50vw;background:#fb7d7d;width: 100px;height: 100px;">
{{ message }}
</div>`;
var el = document.createElement("div");
el.innerHTML = text;
// 放置到body的末尾
document.body.append(el);
const App = {
// vue固定写法
// 我们在这个对象内填入数据,Vue将会从这个对象里寻找数据。

// 这里之所以需要函数返回,是因为放了一个页面同时启用多个组件,导致其数据发生共
// 一个组件修改数据,N个相同的组件同样修改数据,因为对象是引用的,所以会发生这种情况。

// 而使用函数进行返回对象,相当于函数做了一个生成器,每次返回的对象都是再次生成的
// 这样每个组件都有一个属于自己的独立数据,不用担心出现数据污染问题。
data() {
return {
message: "Hello World",
};
},
};
// 对createApp传入了App对象,然后会返回给我们一个对象,
// 我们调用这个对象的mount进行挂载,这部分属于固定写法
const app = Vue.createApp(App);
app.mount("#app");
};

通过 Require 进行引用

错误尝试

我们先尝试引入一下

警告

下方代码是错误代码,正确代码请参阅文章最后的最终完整代码

// ==UserScript==
// @name require引入Vue
// @namespace http://tampermonkey.net/
// @version 0.1
// @author LHD
// @require https://unpkg.com/vue@next
// @match https://bbs.tampermonkey.net.cn/*
// @grant none
// ==/UserScript==

let text = `<div id="app" style="position: absolute;top: 50vh;left: 50vw;background:#fb7d7d;width: 100px;height: 100px;">
{{ message }}
</div>`;
var el = document.createElement("div");
el.innerHTML = text;
document.body.append(el);
const App = {
data() {
return {
message: "Hello World",
};
},
};
const app = Vue.createApp(App);
app.mount("#app");

发现触发了报错

Uncaught ReferenceError: Vue is not defined
anonymous line 15777 > Function:3
VueJS 12
value
value
set

报错分析

这个时候通常是最后一行导致的错误,我们看看是哪行代码发生了错误

function anonymous() {
const _Vue = Vue; //ReferenceError: Vue is not defined
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString } = _Vue;
return _toDisplayString(message);
}
};
}

通过分析,报错原因是找不到 Vue 的变量。

我们进一步观察一下 Vue 源码

var Vue = (function (exports) {
code;
})();

这里可以发现 var Vue 是一个自执行函数的返回结果。

自执行函数

自执行函数就是运行到这个函数上会开始自动调用函数,并且返回结果例如

const a = (function test(a, b) {
return (sum = a + b);
})(1, 2);
console.log(a); // 3

转回正题,这里 var Vue 就是自执行函数的返回结果

那我们冷静思考一下,到底我们在网页中与在油猴脚本中有什么不同?

仔细想想,答案就显而易见了:作用域 (括号里这句话的作用是让答案看起来更长)

如果在直接在网页中插入 script 标签加载,var Vue 属于全局作用域,可以在 window 下被找到

而在油猴代码的执行过程中,无论 none 还是 unsafewindow 哪种情况,其执行过程都是

function sandbox(a, b, c, d) {
// require引入的代码
// UserScript声明头部分
// 脚本代码
}

也就是说这个 var Vue 的作用域并不在全局作用域,而是在 sandbox 函数中。

解决方案

那我们可以尝试挂载到 window 下。

因为我们的脚本代码跟 require 引入的代码都在一个 sandbox 函数中,所以这个时候脚本代码是可以拿到 vue 变量的。

我们直接使用unsafeWindow.Vue=Vue将其挂载到 window 上,再尝试运行,发现已经可以正常运行。

最终完整代码

完整代码如下

// ==UserScript==
// @name require引入Vue
// @namespace http://tampermonkey.net/
// @version 0.1
// @author LHD
// @require https://unpkg.com/vue@next
// @match https://bbs.tampermonkey.net.cn/*
// @grant unsafeWindow
// ==/UserScript==

unsafeWindow.Vue = Vue;

let text = `<div id="app" style="position: absolute;top: 50vh;left: 50vw;background:#fb7d7d;width: 100px;height: 100px;">
{{ message }}
</div>`;
var el = document.createElement("div");
el.innerHTML = text;
document.body.append(el);
const App = {
data() {
return {
message: "Hello World",
};
},
};
const app = Vue.createApp(App);
app.mount("#app");

另一种办法是使用 require 来引入这行代码。我们可以使用data:application/javascript协议来引入一段 javascript 代码,代码需要经过 URL 编码。

引入的代码是unsafeWindow.Vue=Vue;

完整代码如下:

// ==UserScript==
// @name require引入Vue
// @namespace http://tampermonkey.net/
// @version 0.1
// @author LHD
// @require https://unpkg.com/vue@next
// highline-next-line
// @require data:application/javascript,unsafeWindow.Vue%3DVue%3B
// @match https://bbs.tampermonkey.net.cn/*
// @grant unsafeWindow
// ==/UserScript==

let text = `<div id="app" style="position: absolute;top: 50vh;left: 50vw;background:#fb7d7d;width: 100px;height: 100px;">
{{ message }}
</div>`;
var el = document.createElement("div");
el.innerHTML = text;
document.body.append(el);
const App = {
data() {
return {
message: "Hello World",
};
},
};
const app = Vue.createApp(App);
app.mount("#app");