十一、指令、插件、SSR 和其它
现在你进入职业联赛了!您是一名高级 Vue 开发人员。让我们有一些乐趣,看看一些伟大的食谱是定制为您!以下是一些精心挑选的优化解决方案,它们可以提高 Vue 应用的质量,让您的生活更轻松。
在本章中,我们将介绍以下配方:
- 自动加载
vue-router
路线 - 自动加载
vuex
模块 - 创建自定义指令
- 创建 Vue 插件
- 利用类星体在 Vue 中创建 SSR、SPA、PWA、Cordova 和电子应用
- 创建更智能的 Vue 监视程序和计算属性
- 以 Python
Flask
为 API 创建Nuxt.js
SSR - Vue 应用的注意事项
技术要求
在本章中,我们将使用 Node.js、[T0]、[T1]、[T2]、[T3]、[T4]和 Python。
**Attention Windows users: you are required to install an npm
package called windows-build-tools
to be able to install the following required packages. To do so, open PowerShell as an Administrator and execute the following command:
> npm install -g windows-build-tools
要安装Vue-CLI
,您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g @vue/cli @vue/cli-service-global
要安装Cordova
,您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g cordova
如果您在 macOS 上运行并且希望运行 iOS 模拟器,则需要在终端(macOS)中执行以下命令:
> npm install -g ios-sim ios-deploy
要安装Electron
、您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g electron
要安装Quasar
、您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g @quasar/cli
要安装Nuxt.js
、您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g create-nuxt-app
自动加载 Vue 路由
为了创建可维护的代码,我们可以在项目中使用自动导入具有相同结构的文件的策略。就像vue-router
中的路由一样,当应用变大时,我们会发现大量文件被手动导入和处理。在本食谱中,我们将学习一个技巧,使用 webpackrequire.context
函数为我们自动注入文件。
此函数将读取文件内容,并将路由添加到默认情况下将导出到文件中的数组中。您可以通过添加更受控制的路由导入或甚至基于环境的路由规则来改进此配方。
准备
此配方的先决条件如下:
- Node.js 12+
所需的 Node.js 全局对象如下:
@vue/cli
@vue/cli-service-global
我们需要使用Vue-CLI
创建一个新的 Vue 项目,或者使用在以前的配方中创建的项目:
- 我们需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> vue create router-import
-
CLI 将询问一些有助于创建项目的问题。您可以使用箭头键进行导航,输入键继续,空格键选择一个选项。
-
启动新项目有两种方法。默认方法是一个基本的
babel
和eslint
项目,没有任何插件或配置,Manually
模式,您可以选择更多的模式、插件、过梁和选项。我们将选择Manually
:
? Please pick a preset: (Use arrow keys)
default (babel, eslint) ❯ Manually select features
- 现在,我们被问及我们希望在项目中使用的功能。这些特性是一些 Vue 插件,如
Vuex
或Router
(vue-router
)、测试器、linter 等等。选择Babel
、Router
和Linter / Formatter
:
? Check the features needed for your project: (Use arrow keys) ❯ Babel
TypeScript
Progressive Web App (PWA) Support ❯ Router
Vuex
CSS Pre-processors ❯ Linter / Formatter
Unit Testing
E2E Testing
- 通过选择 linter 和 formatter 继续此过程。在本例中,我们将选择
ESLint + Airbnb
配置:
? Pick a linter / formatter config: (Use arrow keys)
ESLint with error prevention only ❯ ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
- 在设置了 linting 规则之后,我们需要定义它们何时应用于代码。可应用于
on save
或固定于on commit
:
? Pick additional lint features: (Use arrow keys)
Lint on save ❯ Lint and fix on commit
- 在定义了所有这些插件、linter 和处理器之后,我们需要选择存储设置和配置的位置。存储它们的最佳位置是在专用文件中,但也可以将它们存储在
package.json
:
? Where do you prefer placing config for Babel, ESLint, etc.? (Use
arrow keys) ❯ In dedicated config files
In package.json
- 现在,您可以选择是否要将此选择设置为未来项目的预设,以便无需重新选择所有内容:
? Save this as a preset for future projects? (y/N) n
Vue-CLI
将创建项目,并自动为我们安装软件包。
如果要在安装完成后在vue-ui
上检查项目,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),并执行以下命令:
> vue ui
也可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)运行内置的npm
命令,并执行以下命令之一:
npm run serve
-在本地运行开发服务器npm run build
-构建并缩小部署应用npm run lint
-在代码上执行 lint
怎么做。。。
按照以下说明在项目中创建路由文件的自动导入,以处理特定文件夹中的路由文件:
- 创建路由文件并将其放入
routes
文件夹后,我们需要确保每个路由文件中都有一个默认的export
对象。在index.js
文件中的src/router
文件夹中,删除文件中存在的routes
的默认数组:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
export default new VueRouter({});
- 现在创建一个空的[T0]数组,该数组将由从文件夹中导入的数组填充,并开始导入。这样,
requireRoutes
将是一个对象,键是文件名,值是文件 ID:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [];
const requireRoutes = require.context(
'./routes',
true,
/^(?!.*test).*\.js$/is,
);
const router = new VueRouter({
routes,
});
export default router;
- 要将这些文件推送到
routes
数组中,我们需要添加以下代码,并在router
文件夹中创建一个名为routes
的文件夹:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [];
const requireRoutes = require.context(
'./routes',
true,
/^(?!.*test).*\.js$/is,
);
requireRoutes.keys().forEach((fileName) => {
routes.push({
...requireRoutes(fileName).default,
});
});
const router = new VueRouter({
routes,
});
export default router;
现在,当您在routes
文件夹中创建一个新的.js
文件时,您的路由将自动加载到应用中。
它是如何工作的。。。
require.context
是一个网页包内置函数,允许您传入一个目录进行搜索,一个指示是否也应检查子目录的标志,以及一个匹配文件的正则表达式。
当构建过程开始时,webpack 将搜索所有require.context
函数并预执行它们,因此导入所需的文件将在那里进行最终构建。
我们将三个参数传递给函数:第一个参数是开始搜索的文件夹,第二个参数询问搜索是否将转到降序文件夹,最后一个参数是用于文件名匹配的正则表达式。
在此配方中,为了自动加载路由作为函数的第一个参数,我们为文件夹定义了./routes
。作为函数的第二个参数,我们将false
定义为不在子目录中搜索。最后,作为第三个参数,我们将[T2]定义为正则表达式,用于搜索[T3]文件,并忽略名称中包含[T4]的文件。
还有更多。。。
有了这个方法,通过使用路由模块的子目录和用于路由控制的环境,可以将应用提升到下一个级别。
通过这些增量,可以将函数提取到另一个文件中,但在router.js
中,仍需要将其导入main.js
文件中。或者,您可以获取import
函数,并将routes
的数组传递给router.js
。
另见
在[T1]的网页包文档中阅读更多关于网页包依赖关系管理和[T0]的信息 https://webpack.js.org/guides/dependency-management/.
自动加载 Vuex 模块
有时,当我们在一个大项目中工作时,我们需要管理大量导入的Vuex
模块和存储。要处理这些模块,我们始终需要通过创建一个将导入所有文件的文件来导入它们,然后将这些文件导出到 Vuex 存储创建中。
在本教程中,我们将了解一个函数,该函数使用 webpackrequire.context
函数自动加载这些文件并将其注入 Vuex 存储创建中。
准备
此配方的先决条件如下:
- Node.js 12+
所需的 Node.js 全局对象如下:
@vue/cli
@vue/cli-service-global
我们需要使用Vue-CLI
创建一个新的 Vue 项目,或者使用在以前的配方中创建的项目:
- 我们需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> vue create vuex-import
-
CLI 将询问一些有助于创建项目的问题。您可以使用箭头键进行导航,输入键继续,空格键选择一个选项。
-
启动新项目有两种方法。默认方法是一个基本的
babel
和eslint
项目,没有任何插件或配置,Manually
模式,您可以选择更多的模式、插件、过梁和选项。我们将选择Manually
:
? Please pick a preset: (Use arrow keys)
default (babel, eslint) ❯ Manually select features
- 现在,我们被问及该项目需要哪些功能。这些特性是一些 Vue 插件,如
Vuex
或Router
(vue-router
)、测试器、linter 等等。选择Babel
、Vuex
和Linter / Formatter
:
? Check the features needed for your project: (Use arrow keys) ❯ Babel
TypeScript
Progressive Web App (PWA) Support
Router ❯Vuex
CSS Pre-processors ❯ Linter / Formatter
Unit Testing
E2E Testing
- 通过选择 linter 和 formatter 继续此过程。在本例中,我们将选择
ESLint + Airbnb
配置:
? Pick a linter / formatter config: (Use arrow keys)
ESLint with error prevention only ❯ ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
- 在设置了 linting 规则之后,我们需要定义它们何时应用于代码。可应用于
on save
或固定于on commit
:
? Pick additional lint features: (Use arrow keys)
Lint on save ❯ Lint and fix on commit
- 在定义了所有这些插件、linter 和处理器之后,我们需要选择存储设置和配置的位置。存储它们的最佳位置是在专用文件中,但也可以将它们存储在
package.json
:
? Where do you prefer placing config for Babel, ESLint, etc.? (Use
arrow keys) ❯ In dedicated config files
In package.json
- 现在,您可以选择是否要将此选择作为未来项目的预设,因此无需重新选择所有内容:
? Save this as a preset for future projects? (y/N) n
Vue-CLI
将创建项目,并自动为我们安装软件包。
如果要在安装完成后在vue-ui
上检查项目,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),并执行以下命令:
> vue ui
也可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令之一来运行内置的npm
命令:
npm run serve
-在本地运行开发服务器npm run build
-构建并缩小部署应用npm run lint
-在代码上执行 lint
怎么做。。。
按照以下说明在项目中创建vuex
模块的自动导入,该模块将处理特定文件夹中的路由文件:
- 创建路由文件并将其放入
store
文件夹后,我们需要确保每个store
文件中都有一个默认的export
对象。在index.js
文件中,在src/store
文件夹中,我们需要提取stores
或modules
的数组:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({});
- 在
src/store
文件夹中创建另一个名为loader.js
的文件(这将是我们的module
加载器)。重要的是要记住,在使用这个配方时,您将使用vuex
名称空间,因为所有stores
都需要作为一个模块使用,并且需要在单个 JavaScript 对象中导出。每个文件名都将用作对命名空间的引用,并将其解析为 camelCase 文本样式:
const toCamel = (s) => s.replace(/([-_][a-z])/ig, (c) =>
c.toUpperCase()
.replace(/[-_]/g, ''));
const requireModule = require.context('./modules/', false,
/^(?!.*test).*\.js$/is);
const modules = {};
requireModule.keys().forEach((fileName) => {
const moduleName = toCamel(fileName.replace(/(\.\/|\.js)/g, ''));
modules[moduleName] = {
namespaced: true,
...requireModule(fileName).default,
};
});
export default modules;
- 由于默认情况下我们将导入
modules
文件夹中的每个文件,因此一个好的做法是为每个模块创建一个文件。例如,当您将创建一个名为user
的模块时,您需要创建一个名为user.js
的文件,该文件导入所有stores
操作、突变、getter 和状态。这些可以放在与模块同名的文件夹中。modules
文件夹的结构与此类似:
modules
├── user.js
├── user
│ └── types.js
│ └── state.js
│ └── actions.js
│ └── mutations.js
│ └── getters.js
└───────
src/store/modules
文件夹中的user.js
文件如下所示:
import state from './user/state';
import actions from './user/actions';
import mutations from './user/mutations';
import getters from './user/getters';
export default {
state,
actions,
mutations,
getters,
};
- 在
src/store
文件夹的index.js
文件中,我们需要添加自动加载的导入模块:
import Vue from 'vue';
import Vuex from 'vuex';
import modules from './loader';
Vue.use(Vuex);
export default new Vuex.Store({
modules,
});
现在,当您在src/store/modules
文件夹中创建新的.js
文件时,您的vuex
模块将自动加载到应用中。
它是如何工作的。。。
require.context
是一个网页包内置函数,它接收执行搜索的目录、指示此搜索中是否包含子目录的布尔标志,以及文件名模式匹配的正则表达式(都作为参数)。
当构建过程开始时,webpack 将搜索所有的require.context
函数,并预执行它们,因此导入所需的文件将在那里进行最终构建。
在我们的例子中,我们为文件夹传递了./modules
,为不在子目录中搜索传递了false
,为正则表达式传递了/^(?!.*test).*\.js$/is
来搜索.js
文件,并忽略名称中包含.test
的文件。
然后,函数将搜索文件,并通过for
循环将结果传递给vuex
模块数组中的文件内容。
另见
在[T1]的网页包文档中阅读更多关于网页包依赖关系管理和[T0]的信息 https://webpack.js.org/guides/dependency-management/.
创建自定义指令
谈到诸如 Vue 之类的可视化框架,我们总是考虑组件、渲染和可视化元素,而我们忘记了除了组件本身之外还有很多东西。
有一些指令使组件与模板引擎一起工作,模板引擎是数据和可视化结果之间的绑定代理。Vue 的核心有内置指令,如v-if
、v-else
和v-for
。
在本食谱中,我们将学习如何制作指令。
准备
此配方的先决条件如下:
- Node.js 12+
所需的 Node.js 全局对象如下:
@vue/cli
@vue/cli-service-global
我们需要使用Vue-CLI
创建一个新的 Vue 项目,或者使用在以前的配方中创建的项目:
- 我们需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> vue create vue-directive
-
CLI 将询问一些有助于创建项目的问题。您可以使用箭头键导航,输入键继续,使用 s起搏器选择选项。
-
启动新项目有两种方法。默认方法是一个基本的
babel
和eslint
项目,没有任何插件或配置,Manually
模式,您可以选择更多的模式、插件、过梁和选项。我们将选择Manually
:
? Please pick a preset: (Use arrow keys)
default (babel, eslint) ❯ Manually select features
- 现在,我们被问及我们希望在项目中使用的功能。这些特性是一些 Vue 插件,如
Vuex
或Router
(vue-router
)、测试器、linter 等等。选择Babel
和Linter / Formatter
:
? Check the features needed for your project: (Use arrow keys) ❯ Babel
TypeScript
Progressive Web App (PWA) SupportRouter
Vuex
CSS Pre-processors ❯ Linter / Formatter
Unit Testing
E2E Testing
- 通过选择 linter 和 formatter 继续此过程。在本例中,我们将选择
ESLint + Airbnb
配置:
? Pick a linter / formatter config: (Use arrow keys)
ESLint with error prevention only ❯ ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
- 在设置了 linting 规则之后,我们需要定义它们何时应用于代码。可应用于
on save
或固定于on commit
:
? Pick additional lint features: (Use arrow keys)
Lint on save ❯ Lint and fix on commit
- 在定义了所有这些插件、linter 和处理器之后,我们需要选择存储设置和配置的位置。存储它们的最佳位置是在专用文件中,但也可以将它们存储在
package.json
:
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys) ❯ In dedicated config files
In package.json
- 现在,您可以选择是否要将此选择设置为未来项目的预设,以便不必重新选择所有内容:
? Save this as a preset for future projects? (y/N) n
Vue-CLI
将创建项目,并自动为我们安装软件包。
如果要在安装完成后在vue-ui
上检查项目,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),并执行以下命令:
> vue ui
或者,您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令之一来运行内置的 npm 命令:
npm run serve
-在本地运行开发服务器npm run build
-构建并缩小部署应用npm run lint
-在代码上执行 lint
怎么做。。。
按照以下说明为屏蔽输入字段创建指令:
-
在
src/directives
文件夹中创建一个名为formMaskInputDirective.js
的文件,在同一文件夹中创建一个名为tokens.js
的文件。 -
在
tokens.js
文件中,我们将添加我们的面具基础标记。这些标记将用于标识我们的输入将接受的价值类型:
export default {
"#": { pattern: /[\x2A\d]/ },
0: { pattern: /\d/ },
9: { pattern: /\d/ },
X: { pattern: /[0-9a-zA-Z]/ },
S: { pattern: /[a-zA-Z]/ },
A: { pattern: /[a-zA-Z]/, transform: v => v.toLocaleUpperCase() },
a: { pattern: /[a-zA-Z]/, transform: v => v.toLocaleLowerCase() },
"!": { escape: true }
};
- 我们从
token.js
导入代币并创建我们的功能:
import tokens from './tokens';
function maskerValue() {
// Code will be developed in this recipe
}
function eventDispatcher() {
// Code will be developed in this recipe
}
function maskDirective() {
// Code will be developed in this recipe
}
export default maskDirective;
- 在
maskDirective
函数中,我们需要检查指令被调用方传递的指令上的绑定值,并检查它是否为有效绑定。为此,我们将首先检查binding
参数上是否存在value
属性,然后使用导入的tokens
将其添加到config
变量中:
function maskDirective(el, binding) {
let config;
if (!binding.value) return false;
if (typeof config === 'string') {
config = {
mask: binding.value,
tokens,
};
} else {
throw new Error('Invalid input entered');
}
- 现在我们需要检查元素并验证它是否是一个
input
HTML 元素。为此,我们将检查该指令传递的元素是否具有tagName
的input
,如果没有,我们将尝试在传递的元素中找到input
HTML 元素:
let element = el;
if (element.tagName.toLocaleUpperCase() !== 'INPUT') {
const els = element.getElementsByTagName('input');
if (els.length !== 1) {
throw new Error(`v-input-mask directive requires 1 input,
found ${els.length}`);
} else {
[element] = els;
}
}
- 现在我们需要在元素的输入中添加一个事件侦听器。侦听器将调用两个外部函数,一个用于调度事件,另一个用于将屏蔽值返回到输入:
element.oninput = (evt) => {
if (!evt.isTrusted) return;
let position = element.selectionEnd;
const digit = element.value[position - 1];
element.value = maskerValue(element.value, config.mask,
config.tokens);
while (
position < element.value.length
&& element.value.charAt(position - 1) !== digit
) {
position += 1;
}
if (element === document.activeElement) {
element.setSelectionRange(position, position);
setTimeout(() => {
element.setSelectionRange(position, position);
}, 0);
}
element.dispatchEvent(eventDispatcher('input'));
};
const newDisplay = maskerValue(element.value, config.mask,
config.tokens);
if (newDisplay !== element.value) {
element.value = newDisplay;
element.dispatchEvent(eventDispatcher('input'));
}
return true;
}
// end of maskDirective function
- 让我们创建
eventDispatcher
函数;此函数将发出v-on
指令将监听的事件:
function eventDispatcher(name) {
const evt = document.createEvent('Event');
evt.initEvent(name, true, true);
return evt;
}
- 现在是复杂的部分:将屏蔽输入值返回到输入。为此,我们需要创建
maskerValue
函数。此函数接收值、掩码和令牌作为参数。函数根据掩码检查当前值,以查看掩码是否完整或值是否为有效标记。如果一切正常,它将把值传递给输入:
function maskerValue(v, m, tkn) {
const value = v || '';
const mask = m || '';
let maskIndex = 0;
let valueIndex = 0;
let output = '';
while (maskIndex < mask.length && valueIndex < value.length) {
let maskCharacter = mask[maskIndex];
const masker = tkn[maskCharacter];
const valueCharacter = value[valueIndex];
if (masker && !masker.escape) {
if (masker.pattern.test(valueCharacter)) {
output += masker.transform ?
masker.transform(valueCharacter) : valueCharacter;
maskIndex += 1;
}
valueIndex += 1;
} else {
if (masker && masker.escape) {
maskIndex += 1;
maskCharacter = mask[maskIndex];
}
output += maskCharacter;
if (valueCharacter === maskCharacter) valueIndex += 1;
maskIndex += 1;
}
}
let outputRest = '';
while (maskIndex < mask.length) {
const maskCharacter = mask[maskIndex];
if (tkn[maskCharacter]) {
outputRest = '';
break;
}
outputRest += maskCharacter;
maskIndex += 1;
}
return output + outputRest;
}
//end of maskerValue function
- 文件准备就绪后,我们需要在
main.js
文件中导入掩码指令,并将该指令添加到 Vue 中,使该指令名为'input-mask'
:
import Vue from 'vue';
import App from './App.vue';
import InputMaskDirective from './directives/formMaskInputDirective';
Vue.config.productionTip = false;
Vue.directive('input-mask', InputMaskDirective);
new Vue({
render: (h) => h(App),
}).$mount('#app');
- 要在我们的应用中使用该指令,我们需要在单个文件组件
<template>
节中的input
HTML 元素上调用该指令,并在v-input-mask
指令中传递token
模板'###-###-###'
,如下所示:
<template>
<div id="app">
<input
type="text"
v-input-mask="'###-###-###'"
v-model="inputMask"
/>
</div>
</template>
<script>
export default {
name: 'app',
data: () => ({
inputMask: '',
}),
};
</script>
它是如何工作的。。。
Vue 指令有五个可能的挂钩。我们只用了一个,bind
。它直接绑定到元素和组件。它有三个参数:element
、binding
和vnode
。
当我们将main.js
文件中的指令添加到 Vue 时,我们使它在应用中的任何地方都可用,因此该指令已经位于App.vue
供输入使用。
同时我们在输入元素上调用v-input-mask
,我们将第一个参数element
传递给指令,第二个参数binding
是属性的值。
我们的指令通过检查输入上的每个新字符值来工作。执行正则表达式测试并验证该字符,以查看它是否是指令实例化中给定的令牌列表上的有效字符。然后,如果通过测试,则返回该字符;如果该字符无效,则不返回任何内容。
创建 Vue 插件
有时需要向应用添加新的内容,并且需要共享此内容。最好的分享方式是使用插件。在 Vue 中,插件是 Vue 全局原型的补充,它使用新功能(如指令、混合、过滤器、原型注入或全新功能)扩展初始化的应用。
现在,我们将学习如何制作插件,以及如何使用插件与 Vue 整体交互(而不会弄乱原型并破坏它)。
准备
此配方的先决条件如下:
- Node.js 12+
所需的 Node.js 全局对象如下:
@vue/cli
@vue/cli-service-global
我们需要使用Vue-CLI
创建一个新的 Vue 项目,或者使用在以前的配方中创建的项目:
- 我们需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> vue create vue-plugin
-
CLI 将询问一些有助于创建项目的问题。您可以使用箭头键导航,输入键继续,使用 s起搏器选择选项。
-
启动新项目有两种方法。默认方法是一个基本的
babel
和eslint
项目,没有任何插件或配置,Manually
模式,您可以选择更多的模式、插件、过梁和选项。我们将选择Manually
:
? Please pick a preset: (Use arrow keys)
default (babel, eslint) ❯ Manually select features
- 现在,我们被问及我们希望在项目中使用的功能。这些特性是一些 Vue 插件,如
Vuex
或Router
(vue-router
)、测试器、linter 等等。选择Babel
和Linter / Formatter
:
? Check the features needed for your project: (Use arrow keys) ❯ Babel
TypeScript
Progressive Web App (PWA) Support
Router
Vuex
CSS Pre-processors ❯ Linter / Formatter
Unit Testing
E2E Testing
- 通过选择 linter 和 formatter 继续此过程。在本例中,我们将选择
ESLint + Airbnb
配置:
? Pick a linter / formatter config: (Use arrow keys)
ESLint with error prevention only ❯ ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
- 在设置了 linting 规则之后,我们需要定义它们何时应用于代码。可应用于
on save
或固定于on commit
:
? Pick additional lint features: (Use arrow keys)
Lint on save ❯ Lint and fix on commit
- 在定义了所有这些插件、linter 和处理器之后,我们需要选择存储设置和配置的位置。存储它们的最佳位置是在专用文件中,但也可以将它们存储在
package.json
:
? Where do you prefer placing config for Babel, ESLint, etc.? (Use
arrow keys) ❯ In dedicated config files
In package.json
- 现在,您可以选择是否要将此选择作为未来项目的预设,因此无需重新选择所有内容:
? Save this as a preset for future projects? (y/N) n
Vue-CLI
将创建项目,并自动为我们安装软件包。
如果要在安装完成后在vue-ui
上检查项目,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),并执行以下命令:
> vue ui
或者,您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令之一来运行内置的 npm 命令:
npm run serve
-在本地运行开发服务器npm run build
-构建并缩小部署应用npm run lint
-在代码上执行 lint
怎么做。。。
编写 Vue 插件很简单,无需了解更多关于 Vue 本身的信息。插件的基本概念是一个需要有install
函数的对象,当Vue.use()
方法调用该函数时将执行该函数。install
函数将接收两个参数:Vue 和用于实例化插件的选项。
按照以下说明编写插件,为 Vue 全局原型添加两个新功能$localStorage
和$sessionStorage
:
- 在我们的项目中,我们需要在名为
storageManipulator.js
的src/plugin
文件夹中创建一个文件。 - 在此文件中,我们将创建插件安装对象–我们将添加默认插件选项和功能的基本原型:
/* eslint no-param-reassign: 0 */
const defaultOption = {
useSaveFunction: true,
useRetrieveFunction: true,
onSave: value => JSON.stringify(value),
onRetrieve: value => JSON.parse(value),
};
export default {
install(Vue, option) {
const baseOptions = {
...defaultOption,
...option,
};
Vue.prototype.$localStorage = generateStorageObject(
window.localStorage,
baseOptions,
); // We will add later this code
Vue.prototype.$sessionStorage = generateStorageObject(
window.localStorage,
baseOptions,
); // We will add later this code
},
};
- 现在我们需要创建
generateStorageObject
函数。此函数将接收两个参数:第一个是窗口存储对象,第二个是插件选项。这样,就可以生成将注入 Vue 的原型上使用的对象:
const generateStorageObject = (windowStorage, options) => ({
set(key, value) {
windowStorage.setItem(
key,
options.useSaveFunction
? options.onSave(value)
: value,
);
},
get(key) {
const item = windowStorage.getItem(key);
return options.useRetrieveFunction ? options.onRetrieve(item) :
item;
},
remove(key) {
windowStorage.removeItem(key);
},
clear() {
windowStorage.clear();
},
});
- 我们需要将插件导入到
main.js
中,然后使用Vue.use
功能将插件安装到我们的 Vue 应用中:
import Vue from 'vue';
import App from './App.vue';
import StorageManipulatorPlugin from './plugin/storageManipulator';
Vue.config.productionTip = false;
Vue.use(StorageManipulatorPlugin);
new Vue({
render: h => h(App),
}).$mount('#app');
现在,您可以在 Vue 应用中的任何位置使用该插件,调用this.$localStorage
方法或this.$sessionStorage
。
它是如何工作的。。。
Vue 插件的工作原理是将指示要使用的所有代码添加到 Vue 应用层(如 mixin)。
当我们使用Vue.use()
导入插件时,我们告诉 Vue 调用导入文件对象上的install()
函数并执行它。Vue 将自动将当前 Vue 作为第一个参数传递,并将选项(如果已声明)作为第二个参数传递。
在我们的插件中,当调用install()
函数时,我们首先创建baseOptions
,将默认选项与传递的参数合并,然后向 Vue 原型中注入两个新属性。这些属性现在随处可见,因为传递的Vue
参数是应用中使用的Vue global
。
我们的generateStorageObject
是浏览器存储 API 的纯粹抽象。我们将其用作插件中原型的生成器。
另见
有关 Vue 插件的更多信息,请访问https://vuejs.org/v2/guide/plugins.html.
你可以在找到一份精心策划的 Vue 插件列表 https://github.com/vuejs/awesome-vue.
利用类星体在 Vue 中创建 SSR、SPA、PWA、Cordova 和电子应用
Quasar 是一个基于 Vue 和材料设计的框架,它利用了“一次编写,到处使用”的优势
CLI 可以将相同的代码库部署到不同的风格,例如单页应用(SPA)、服务器端渲染(SSR)、渐进式 Web 应用(PWA、移动应用(Cordova)和桌面应用(电子版)。
这就消除了开发人员的一些问题,例如使用HMR(热模块重新加载)配置 webpack、Cordova 和 Electron 进行开发,或者在 SPA 项目中添加 SSR 配置。该框架帮助开发人员尽快开始生产。
在本教程中,我们将学习如何使用 Quasar 和 CLI 创建基本项目,以及如何使用 CLI 添加 SPA、PWA、SSR、移动应用和桌面应用的开发目标。
准备
此配方的先决条件如下:
- Node.js 12+
所需的 Node.js 全局对象如下:
@quasar/cli
我们需要使用 Quasar CLI 创建一个新的 Quasar 项目,或者使用在以前的配方中创建的项目。
为此,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar create quasar-project
现在,当被询问时,我们需要选择手动选择功能:
Quasar-CLI
将询问您项目名称。定义项目名称。在我们的案例中,我们选择quasar_project
:
> Project name: quasar_project
- 然后
Quasar-CLI
将询问项目产品名称。这将由移动应用用于定义其标题名称。在我们的案例中,我们使用提供的默认名称:
> Project product name (must start with letter if building mobile
apps) (Quasar App)
- 现在
Quasar-CLI
将要求提供项目说明。当共享页面时,这用于搜索引擎中的元标记。在我们的案例中,我们使用了提供的默认描述:
> Project description: (A Quasar Framework app)
- 然后
Quasar-CLI
将询问项目作者。请在此填写一个package.json
有效名称(例如Heitor Ribeiro<heitor@example.com>
:
> Author: <You>
- 现在是选择 CSS 预处理器的时候了。在我们的情况下,我们将使用
Sass with indented syntax
:
Pick your favorite CSS preprocessor: (can be changed later) (Use arrow keys) ❯ Sass with indented syntax (recommended)
Sass with SCSS syntax (recommended)
Stylus
None (the others will still be available)
- 然后
Quasar-CLI
将询问组件和指令的进口策略。我们将使用默认的auto-import
策略:
Pick a Quasar components & directives import strategy: (can be
changed later) (Use arrow keys ) ❯ * Auto-import in-use Quasar components & directives - also
treeshakes Quasar; minimum bundle size
* Import everything from Quasar - not treeshaking Quasar;
biggest bundle size
- 现在我们需要为项目选择额外的功能。我们将选择
EsLint
:
Check the features needed for your project: EsLint
- 之后,
Quasar-CLI
将要求为 ESLint 设置预设。选择Airbnb
预设:
Pick an ESLint preset: Airbnb
- 最后,
Quasar-CLI
将请求您要使用的应用来安装项目的依赖项。在我们的例子中,我们使用了yarn
,因为我们已经安装了它(但您可以选择您喜欢的一个):
Continue to install project dependencies after the project has been
created? (recommended) (Use arrow keys) ❯ Yes, use Yarn (recommended)
Yes, use npm
No, I will handle that myself
现在在 IDE 或代码编辑器中打开创建的文件夹。
怎么做。。。
当使用 Quasar 创建应用时,您总是需要选择一种风格来启动,但主代码将是 SPA。因此,其他口味的人会根据他们的需求提供特殊的食物和美味,但是您可以根据构建环境对构建进行个性化设置并使构建执行一些代码。
开发 SPA(单页应用)
SPA 是开箱即用的解决方案;无需添加任何新配置。
让我们开始向应用添加一个新页面。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar new page About
Quasar-CLI
将自动为我们创建 Vue 页面。我们需要在路由文件中添加对页面的引用,该页面将在应用上可用:
- 为此,我们需要打开
src/router
文件夹中的routes.js
文件,并添加About
页面:
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', name: 'home', component: () =>
import('pages/Index.vue') },
{ path: 'about', name: 'about', component: () =>
import('pages/About.vue') },
],
},
{
path: '*',
component: () => import('pages/Error404.vue'),
}
];
export default routes;
- 然后打开
src/pages
文件夹中的About.vue
文件。您会发现该文件是一个单一的文件组件,其中有一个空的QPage
组件,因此我们需要在<template>
部分添加一个基本的标题和页面指示:
<template>
<q-page
padding
class="flex flex-start"
>
<h1 class="full-width">About</h1>
<h2>This is an About Us Page</h2>
</q-page>
</template>
<script>
export default {
name: 'PageAbout',
};
</script>
- 现在,在
MainLayout.vue
文件的src/layouts
文件夹中,到q-drawer
组件,我们需要添加到Home
和About
页面的链接:
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-btn flat dense round
@click="leftDrawerOpen = !leftDrawerOpen"
aria-label="Menu">
<q-icon name="menu" />
</q-btn>
<q-toolbar-title>
Quasar App
</q-toolbar-title>
<div>Quasar v{{ $q.version }}</div>
</q-toolbar>
</q-header>
<q-drawer v-model="leftDrawerOpen"
bordered content-class="bg-grey-2">
<q-list>
<q-item-label header>Menu</q-item-label>
<q-item clickable tag="a" :to="{name: 'home'}">
<q-item-section avatar>
<q-icon name="home" />
</q-item-section>
<q-item-section>
<q-item-label>Home</q-item-label>
</q-item-section>
</q-item>
<q-item clickable tag="a" :to="{name: 'about'}">
<q-item-section avatar>
<q-icon name="school" />
</q-item-section>
<q-item-section>
<q-item-label>About</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-drawer>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
</template>
<script>
export default {
name: "MyLayout",
data() {
return {
leftDrawerOpen: this.$q.platform.is.desktop
};
}
};
</script>
我们以一个在类星体框架内运行的 SPA 的简单示例结束。
命令
您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作之一来运行Quasar-CLI
命令:
quasar dev
-启动开发模式quasar build
-建造水疗中心
开发 PWA(渐进式 Web 应用)
为了开发 PWA,我们首先需要通知 Quasar,我们想要添加一种新的开发模式。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar mode add pwa
Quasar-CLI
将创建一个名为src-pwa
的文件夹,其中包含我们的service-workers
文件,与主代码分离。
要清理新添加的文件并将其转换为 Airbnb 格式,我们需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),然后执行以下命令:
> eslint --fix --ext .js ./src-pwa
我们添加到 SPA 的代码仍将用作我们的基础,以便我们也可以添加新的页面、组件和其他功能,这些功能将在 PWA 上使用。
So, are you wondering why service-worker
is not in the main src
folder? This is because those files are exclusively for PWAs, and are not needed in any other case than this one. The same will happen in different build types, such as Electron, Cordova, and SSR.
在 PWA 上配置 quasar.conf
对于 PWA 开发,您可以在root
文件夹中的quasar.conf.js
文件上设置一些特殊标志:
pwa: {
// workboxPluginMode: 'InjectManifest',
// workboxOptions: {},
manifest: {
// ...
},
// variables used to inject specific PWA
// meta tags (below are default values)
metaVariables: {
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'default',
appleTouchIcon120: 'statics/icons/apple-icon-120x120.png',
appleTouchIcon180: 'statics/icons/apple-icon-180x180.png',
appleTouchIcon152: 'statics/icons/apple-icon-152x152.png',
appleTouchIcon167: 'statics/icons/apple-icon-167x167.png',
appleSafariPinnedTab: 'statics/icons/safari-pinned-tab.svg',
msapplicationTileImage: 'statics/icons/ms-icon-144x144.png',
msapplicationTileColor: '#000000'
}
}
命令
您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作之一来运行Quasar-CLI
命令:
quasar dev -m pwa
-作为 PWA 启动开发模式quasar build -m pwa
-将代码构建为 PWA
开发 SSR(服务器端渲染)
为了发展 SSR,我们首先需要通知 Quasar,我们想增加一种新的发展模式。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar mode add ssr
Quasar-CLI
将创建一个名为src-ssr
的文件夹,其中包含我们的extension
和server
启动程序文件,与主代码分离。
extension
文件不是由babel
传输的,而是在 Node.js 上下文上运行的,因此它与 Express 或Nuxt.js
应用的环境相同。您可以使用服务器插件,例如database
、fileread
和filewrites
。
server
起始文件将是src-ssr
文件夹中的index.js
文件。作为扩展,不被babel
传输,在 Node.js 上下文上运行。对于 HTTP 服务器,它使用 Express、,如果您配置quasar.conf.js
向客户端传递一个 PWA,您可以同时拥有一个带 PWA 的 SSR。
在 SSR 上配置 quasar.conf
对于 SSR 开发,您可以在root
文件夹中的quasar.conf.js
文件上配置一些特殊标志:
ssr: {
pwa: true/false, // should a PWA take over (default: false), or just
// a SPA?
},
命令
您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作之一来运行Quasar-CLI
命令:
quasar dev -m ssr
-作为 SSR 启动开发模式quasar build -m ssr
-将代码构建为 SSRquasar serve
-运行 HTTP 服务器(可用于生产)
开发移动应用(Cordova)
为了发展 SSR,我们首先需要通知 Quasar,我们想增加一种新的发展模式。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar mode add cordova
现在Quasar-CLI
将询问您一些配置问题:
-
Cordova 应用 ID 是什么?
(org.cordova.quasar.app)
-
Cordova 是否可以匿名报告使用统计数据,以便随着时间的推移改进该工具?(是/否)
N
Quasar-CLI
将创建一个名为src-cordova
的文件夹,其中将包含一个 Cordova 项目。
**Cordova 项目的文件夹结构如下所示:
src-cordova/
├── config.xml
├── packages.json
├── cordova-flag.d.ts
├── hooks/
├── www/
├── platforms/
├── plugins/
As a separate project inside Quasar, to add Cordova plugins, you need to call plugman
or cordova plugin add command
inside the src-cordova
folder.
在 Cordova 上配置 quasar.conf
对于 Cordova 开发,您可以在root
文件夹中的quasar.conf.js
文件上设置一些特殊标志:
cordova: {
iosStatusBarPadding: true/false, // add the dynamic top padding on
// iOS mobile devices
backButtonExit: true/false // Quasar handles app exit on mobile phone
// back button
},
命令
您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作之一来运行Quasar-CLI
命令:
If you don't have a Cordova environment already configured on your desktop, you can find more information on how to set it up here: https://quasar.dev/quasar-cli/developing-cordova-apps/preparation#Android-setup.
quasar dev -m cordova -T android
-作为 Android 设备模拟器启动开发模式quasar build -m cordova -T android
-将代码构建为 Androidquasar dev -m cordova -T ios
-作为 iOS 设备模拟器启动开发模式(仅限 macOS)quasar build -m cordova -T ios
-作为 iOS 设备模拟器启动构建模式(仅限 macOS)
开发桌面应用(Electron)
要开发 SSR,我们首先需要通知 Quasar,我们想要添加一种新的开发模式。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar mode add electron
Quasar-CLI
将创建一个名为src-electron
的文件夹,其中将包含一个电子项目。
Electron 项目的文件夹结构如下所示:
src-electron/
├── icons/
├── main-process/
├── electron-flag.d.ts
在icons
文件夹中,您将找到electron-packager
在构建项目时将使用的图标。在main-process
文件夹中将是您的主要电子文件,拼接成两个文件:一个仅在开发时调用,另一个在开发和生产时调用。
在 Electron 上配置 quasar.conf
对于 Electron 开发,您可以在根文件夹的quasar.conf.js
文件上设置一些特殊标志:
electron: {
// optional; webpack config Object for
// the Main Process ONLY (/src-electron/main-process/)
extendWebpack (cfg) {
// directly change props of cfg;
// no need to return anything
},
// optional; EQUIVALENT to extendWebpack() but uses webpack-chain;
// for the Main Process ONLY (/src-electron/main-process/)
chainWebpack (chain) {
// chain is a webpack-chain instance
// of the Webpack configuration
},
bundler: 'packager', // or 'builder'
// electron-packager options
packager: {
//...
},
// electron-builder options
builder: {
//...
}
},
The packager
key uses the API options for the electron-packager
module, and the builder
key uses the API options for the electron-builder
module.
命令
您可以通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作之一来运行Quasar-CLI
命令:
quasar dev -m electron
-以电子方式启动开发模式quasar build -m electron
-将代码构建为 Electron
它是如何工作的。。。
这一切都是可能的,因为 Quasar 框架在 CLI 上为您封装了构建、解析和绑定。您不必担心 Electron、Cordova 甚至 Babel 的网页包和配置。
一个简单的 CLI 命令可以为您生成一个全新的页面、布局、组件、存储、路由,甚至是一个新的构建。由于 CLI 只是 Vue、webpack、Babel 和其他工具的包装,因此您不必只使用 Quasar visual 组件。如果不想使用它们,可以不导入它们并使用 CLI 的强大功能构建应用。
另见
您可以在的文档中查看更多关于类星体框架的信息 https://quasar.dev/introduction-to-quasar.
在阅读更多关于类星体 SPA 开发的信息 https://quasar.dev/quasar-cli/developing-spa/introduction.
在阅读更多关于类星体 PWA 发展的信息 https://quasar.dev/quasar-cli/developing-pwa/introduction.
在阅读更多关于类星体 SSR 发展的信息 https://quasar.dev/quasar-cli/developing-ssr/introduction.
在阅读更多关于使用 Quasar 开发移动应用的信息 https://quasar.dev/quasar-cli/developing-cordova-apps/introduction.
更多关于科尔多瓦项目的信息,请访问https://cordova.apache.org.
在上阅读更多关于使用 Quasar 开发桌面应用的信息 https://quasar.dev/quasar-cli/developing-electron-apps/introduction.
在上阅读更多关于电子项目的信息 https://electronjs.org/.
更多关于electron-packager
的信息,请访问https://github.com/electron/electron-packager.
阅读更多关于“T0”的信息,参见“T1”,https://www.electron.build/. “T2”。
在找到电子构建选项 APIhttps://www.electron.build/configuration/configuration.
创建更智能的 Vue 监视程序和计算属性
在 Vue 中,使用监视程序和计算属性始终是检查和缓存数据的最佳解决方案,但有时这些数据需要特殊处理,或者需要以与预期不同的方式进行操作。有一些方法可以赋予这些 Vue API 新的生命,帮助您的开发和生产效率。
怎么做。。。
我们将此配方分为两类:一类用于观察者,另一类用于计算属性。一些方法通常一起使用,例如non-cached
计算值和deep-watched
值。
观察者
选择这三个观察者配方是为了提高生产率和最终代码质量。使用这些方法可以减少代码重复并提高代码重用。
使用方法名
所有观察者都可以接收方法名而不是函数,从而防止您编写重复的代码。这将帮助您避免重新编写相同的代码,或检查值并将其传递给函数:
<script>
export default {
watch: {
myField: 'myFunction',
},
data: () => ({
myField: '',
}),
methods: {
myFunction() {
console.log('Watcher using method name.');
},
},
};
</script>
即时通话和深入倾听
您可以通过立即传递一个属性,将您的观察者设置为创建后立即执行,并通过调用deep
属性,使其无论值的变化深度如何都能执行:
<script>
export default {
watch: {
myDeepField: {
handler(newVal, oldVal) {
console.log('Using Immediate Call, and Deep Watch');
console.log('New Value', newVal);
console.log('Old Value', oldVal);
},
deep: true,
immediate: true,
},
},
data: () => ({
myDeepField: '',
}),
};
</script>
多处理器
您可以让您的观察者同时执行各种处理程序,而无需将观察处理程序设置为绑定到唯一的函数:
<script>
export default {
watch: {
myMultiField: [
'myFunction',
{
handler(newVal, oldVal) {
console.log('Using Immediate Call, and Deep Watch');
console.log('New Value', newVal);
console.log('Old Value', oldVal);
},
immediate: true,
},
],
},
data: () => ({
myMultiField: '',
}),
methods: {
myFunction() {
console.log('Watcher Using Method Name');
},
},
};
</script>
计算
有时,计算属性只是用作简单的基于缓存的值,但它们的功能更强。这里有两种方法展示了如何提取这种能量。
没有缓存值
通过将cache
属性设置为false
,您可以将计算属性设置为始终更新的值,而不是缓存的值:
<script>
export default {
computed: {
field: {
get() {
return Date.now();
},
cache: false,
},
},
};
</script>
接二连三
您可以向计算属性添加 setter 函数,使其成为完全完整的数据属性,但不绑定到数据。
不建议这样做,但这是可能的,在某些情况下,您可能需要这样做。例如,必须以毫秒为单位保存日期,但需要以 ISO 格式显示。使用此方法,您可以拥有dateIso
属性get
和set
值:
<script>
export default {
data: () => ({
dateMs: '',
}),
computed: {
dateIso: {
get() {
return new Date(this.dateMs).toISOString();
},
set(v) {
this.dateMs = new Date(v).getTime();
},
},
},
};
</script>
另见
有关 Vuewatch
API 的更多信息,请访问https://vuejs.org/v2/api/#watch.
有关 Vuecomputed
API 的更多信息,请访问https://vuejs.org/v2/api/#computed.
使用 Python Flask 作为 API 创建 Nuxt.js SSR
Nuxt.js
是一个服务器端渲染框架,用于渲染服务器上的所有内容并将其加载。通过这个过程,页面可以在呈现之前获得 SEO 和快速 API 获取的功能。
正确地使用它,您可以实现强大的 SPA 或 PWA 以及以前无法实现的其他功能。
在后端,Python 是一种快速稳定的解释型动态语言。有了活跃的用户群和快速的学习曲线,这非常适合服务器 API。
将两者结合在一起,就有可能以尽可能快的速度部署一个强大的应用。
准备
此配方的先决条件如下:
- Node.js 12+
- python
所需的 Node.js 全局对象如下:
create-nuxt-app
要安装create-nuxt-app
,您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> npm install -g create-nuxt-app
对于这个配方的后端,我们将使用Python。此配方所需的 Python 全局对象如下:
flask
flask-restful
flask-cors
要安装flask
、flask-restful
和flask-cors
,您需要在终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)中执行以下命令:
> pip install flask
> pip install flask-restful
> pip install flask-cors
怎么做。。。
我们需要把食谱分成两部分。第一部分是后端部分(或者 API,如果您愿意的话),将使用 Python 和 Flask 完成。第二部分为前端部分,在Nuxt.js
上以 SSR 模式运行。
创建烧瓶 API
我们的 API 服务器将基于 Python Flask 框架。我们需要创建一个服务器文件夹来存储我们的服务器文件,并开始服务器的开发。
您将需要安装以下 Python 软件包。为此,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
- 要安装烧瓶框架,请使用以下命令:
> pip install flask
- 要安装 Flask RESTful 扩展,请使用以下命令:
> pip install flask-restful
- 要安装烧瓶 CORS 扩展,请使用以下命令:
> pip install flask-cors
初始化应用
要创建简单的 RESTful API,我们将创建一个文件并使用 SQLite3 作为数据库:
- 创建名为
server
的文件夹,并在其中创建名为app.py
的文件:
import sqlite3 as sql
from flask import Flask
from flask_restful import Resource, Api, reqparse
from flask_cors import CORS
app = Flask(__name__)
api = Api(app)
CORS(app)
parser = reqparse.RequestParser()
conn = sql.connect('tasks.db')
conn.execute('CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY
KEY AUTOINCREMENT, task TEXT)')
conn.close()
- 然后,我们将创建我们的
ToDo
类,在该类的构造函数上,我们将连接到数据库并选择所有tasks
:
class ToDo(Resource):
def get(self):
con = sql.connect('tasks.db')
cur = con.cursor()
cur.execute('SELECT * from tasks')
tasks = cur.fetchall()
con.close()
return {
'tasks': tasks
}
- 为了实现 RESTful POST 方法,创建一个函数,该函数接收
task
作为参数,并将添加了task
的对象、添加的status
添加到任务列表中,然后将列表返回给用户:
def post(self):
parser.add_argument('task', type=str)
args = parser.parse_args()
con = sql.connect('tasks.db')
cur = con.cursor()
cur.execute('INSERT INTO tasks(task) values ("
{}")'.format(args['task']))
con.commit()
con.close()
return {
'status': True,
'task': '{} added.'.format(args['task'])
}
- 接下来,我们将通过创建一个函数来创建 RESTful PUT 方法,该函数将接收[T0]和[T1]作为函数的参数。然后,此功能将用当前的
id
更新task
,并将更新后的task
和更新后的status
返回给用户:
def put(self, id):
parser.add_argument('task', type=str)
args = parser.parse_args()
con = sql.connect('tasks.db')
cur = con.cursor()
cur.execute('UPDATE tasks set task = "{}" WHERE id =
{}'.format(args['task'], id))
con.commit()
con.close()
return {
'id': id,
'status': True,
'task': 'The task {} was updated.'.format(id)
}
- 然后,通过创建一个函数来创建一个 RESTful 删除方法,该函数将接收将被删除的
task
的ID
,然后将被删除的ID
、status
和task
返回给用户:
def delete(self, id):
con = sql.connect('tasks.db')
cur = con.cursor()
cur.execute('DELETE FROM tasks WHERE id = {}'.format(id))
con.commit()
con.close()
return {
'id': id,
'status': True,
'task': 'The task {} was deleted.'.format(id)
}
- 最后,我们将
ToDo
类作为资源添加到'/'
路由上的 API 中,并初始化应用:
api.add_resource(ToDo, '/', '/<int:id>')
if __name__ == '__main__':
app.run(debug=True)
启动服务器
要启动服务器,需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> python server/app.py
您的服务器将在http://localhost:5000
上运行并侦听。
创建您的 Nuxt.js 服务器
要呈现应用,您需要创建Nuxt.js
应用。使用Nuxt.js``create-nuxt-app
CLI,我们将创建它并为其选择一些选项。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> create-nuxt-app client
然后,将向您询问有关安装过程的一些问题。我们将使用以下方法:
- 当您开始使用
Nuxt-CLI
创建项目时,它将首先询问项目名称。在本例中,我们将选择client
作为名称:
Project Name: client
- 然后,您需要选择将在项目中使用的编程语言。我们将选择
JavaScript
:
> Programming language: (Use arrow keys)
❯ JavaScript
TypeScript
- 接下来,
Nuxt-CLI
将请求用于安装依赖项的包管理器。在我们的案例中,我们选择Yarn
,但您可以选择您喜欢的:
> Package manager: (Use arrow keys)
❯ Yarn
npm
- 现在,
Nuxt-CLI
将要求在项目中使用 UI 框架。从可用列表中选择Bulma
:
> UI Framework: Bulma
- 然后,
Nuxt-CLI
将询问您是否要为项目选择额外的模块。我们将从当前模块列表中选择Axios
:
> Nuxt.JS modules: Axios
Nuxt-CLI
将要求我们在项目中使用的起绒工具;我们将选择None
:
> Choose Linting tools: None
- 然后,
Nuxt-CLI
将询问我们想要在我们的项目上实现的测试框架;我们将选择None
:
> Choose Test Framework: None
- 接下来,
Nuxt-CLI
将询问项目将使用的渲染模式;我们将选择Universal (SSR)
:
> Choose Rendering Mode: Universal (SSR)
Nuxt-CLI
将询问将在建筑结构上使用的部署目标;我们将选择Server (Node.js hosting)
:
> Deployment target: Server (Node.js hosting)
- 最后,
Nuxt-CLI
将询问我们想要使用的开发工具配置;我们将选择jsconfig.json
:
> Development tools: jsconfig.json
CLI 完成安装过程后,我们可以在编辑器或 IDE 上打开client
文件夹。
将 Bulma 添加到全局 CSS
要将 Bulma 添加到应用中,我们需要通过执行以下操作在nuxt
配置文件中声明它:
- 打开
client
文件夹中的nuxt.config.js,
。 - 然后,更新 CSS 属性并添加 Bulma 导入,以使其在应用的全局范围内可用:
export default {
/* We need to change only the css property for now, */
/* the rest we will maitain the same */
/*
** Global CSS
*/
css: ['bulma/css/bulma.css'],
}
配置 axios 插件
要开始创建 API 调用,我们需要在应用中添加axios
插件:
- 为此,我们需要打开根文件夹中的
nuxt.config.js,
文件,并添加axios
属性:
export default {
/* We need to change only the axios property for now, */
/* the rest we will maitain the same */
axios: {},
}
- 在
axios
属性上,添加以下配置属性:HOST
定义为'127.0.0.1'
PORT
定义为'5000'
https
定义为false
debug
定义为true
:
axios: {
HOST: '127.0.0.1',
PORT: '5000',
https: false,
debug: true, // Only on development
},
运行 Nuxt.js 服务器
现在您已经设置好了所有内容,您需要运行服务器并开始查看发生了什么。Nuxt.js
附带了一些现成的预编程npm
脚本。通过打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下操作,可以运行以下命令之一:
npm run dev
-以开发模式运行服务器-
npm run build
-使用网页包构建文件,并缩小 CSS 和 JS 以用于生产 -
npm run generate
-为每条路线生成静态 HTML 页面 npm start
–在运行 build 命令后,在生产环境中启动服务器
创建 TodoList 组件
对于 TodoList 应用,我们需要一个组件来获取任务并删除任务。
单文件组件
在这里,我们将创建单文件组件的<script>
部分:
- 在
client/components
文件夹中,创建一个名为TodoList.vue
的文件并打开它。 - 然后,我们将导出一个
default
JavaScript 对象,其中name
属性定义为TodoList
,然后将beforeMount
生命周期挂钩定义为异步函数。将computed
和methods
属性定义为空 JavaScript 对象。然后,创建一个data
属性,该属性定义为返回 JavaScript 对象的单例函数。在data
属性中,创建一个taskList
属性作为空数组:
export default {
name: 'TodoList',
data: () => ({
taskList: [],
}),
computed: {},
async beforeMount() {},
methods: {},
};
- 在
computed
属性中,创建一个名为taskObject
的新属性。此computed
属性将返回Object.fromEntries(new Map(this.taskList))
的结果:
taskObject() {
return Object.fromEntries(new Map(this.taskList));
},
- 在
methods
属性中,创建一个名为getTask
的新方法–它将是一个异步函数。此方法将从服务器获取任务,然后使用响应定义taskList
属性:
async getTasks() {
try {
const { tasks } = await
this.$axios.$get('http://localhost:5000');
this.taskList = tasks;
} catch (err) {
console.error(err);
}
},
- 然后,创建一个
deleteTask
方法。此方法将是一个异步函数,并将接收一个id
作为参数。使用此参数,将执行 API 执行删除任务,然后执行getTask
方法:
async deleteTask(i) {
try {
const { status } = await
this.$axios.$delete(`http://localhost:5000/${i}`);
if (status) {
await this.getTasks();
}
} catch (err) {
console.error(err);
}
},
- 最后,在
beforeMount
生命周期钩子中,我们将执行getTask
方法:
async beforeMount() {
await this.getTasks();
},
单文件组件
是时候创建单文件组件的<template>
部分了:
- 在
client/components
文件夹中,打开TodoList.vue
文件。 - 在
<template>
部分,创建一个div
HTML 元素,并添加值为box
的class
属性:
<div class="box"></div>
- 作为
div.box
HTML 元素的子元素,创建div
HTML 元素,其中class
属性定义为content
,子元素定义为ol
HTML 元素,属性type
定义为1
:
<div class="content">
<ol type="1"></ol>
</div>
- 作为
ol
HTML 元素的子元素,创建一个li
HTML 元素,将v-for
指令定义为(task, i) in taskObject
,将key
属性定义为变量i
:
<li
v-for="(task, i) in taskObject"
:key="i">
</li>
- 最后,作为
ol
HTML 元素的子元素,添加{{ task }}
作为内部文本,作为文本的同级元素,创建一个button
HTML 元素,class
属性定义为delete is-small
,事件监听器定义为deleteTask
方法,将i
变量作为参数传递:
{{ task }}
<button
class="delete is-small"
@click="deleteTask(i)"
/>
创建 Todo 表单组件
要将任务发送到服务器,我们需要一个表单。这意味着我们需要制作一个表单组件来处理这个问题。
单文件组件
在这里,我们将创建单文件组件的<script>
部分:
- 在
client/components
文件夹中,创建一个名为TodoForm.vue
的文件并打开它。 - 然后,我们将导出一个
default
JavaScript 对象,其中name
属性定义为TodoForm
,然后将methods
属性定义为空 JavaScript 对象。然后,创建一个data
属性,该属性定义为返回 JavaScript 对象的单例函数。在data
属性中,创建一个task
属性作为空数组:
export default {
name: 'TodoForm',
data: () => ({
task: '',
}),
methods: {},
};
- 在
methods
属性中,创建一个名为save
的方法,该方法将是一个异步函数。此方法将task
发送给 API,如果 API 接收到Ok Status
,则会发出一个具有task
和task
属性的'new-task'
事件:
async save() {
try {
const { status } = await
this.$axios.$post('http://localhost:5000/', {
task: this.task,
});
if (status) {
this.$emit('new-task', this.task);
this.task = '';
}
} catch (err) {
console.error(err);
}
},
单文件组件
是时候创建单文件组件的<template>
部分了:
-
在
client/components
文件夹中,打开TodoForm.vue
文件。 -
在
<template>
部分,创建一个div
HTML 元素,并添加值为box
的class
属性:
<div class="box"></div>
- 在
div.box
HTML 元素内部,创建一个div
HTML 元素,其class
属性定义为field has-addons
:
<div class="field has-addons"></div>
- 然后,在
div.field.has-addons
HTML 元素内部,创建一个子div
HTML 元素,将class
属性定义为control is-expanded
,并添加一个子输入 HTML 元素,将v-model
指令定义为task
属性。然后,将class
属性定义为input
、type
属性定义为text
、placeholder
属性定义为ToDo Task
。最后,在@keypress.enter
事件监听器中定义save
方法:
<div class="control is-expanded">
<input
v-model="task"
class="input"
type="text"
placeholder="ToDo Task"
@keypress.enter="save"
>
</div>
- 最后,作为
div.control.is-expanded
HTML 元素的同级,创建一个div
HTML 元素,将class
属性定义为control
,并添加一个子a
HTML 元素,将class
属性定义为button is-info
,在@click
事件侦听器上,将其定义为save
方法。作为a
HTML 元素的内部文本,添加Save Task
文本:
<div class="control">
<a
class="button is-info"
@click="save"
>
Save Task
</a>
</div>
创建布局
现在我们需要创建一个新的布局,将应用包装为一个简单的高阶组件。在client/layouts
文件夹中,打开名为default.vue
的文件,删除文件的<style>
部分,并将<template>
部分更改为以下内容:
<template>
<nuxt />
</template>
创建页面
现在,我们将创建应用的主页,用户将能够在其中查看他们的TodoList
并添加一个新的TodoItem
。
单文件组件
在这里,我们将创建单文件组件的<script>
部分:
- 打开
client/pages
文件夹中的index.vue
文件。 - 导入我们创建的
todo-form
和todo-list
组件,然后我们将导出一个default
JavaScript 对象,带有components
属性和导入的组件:
<script>
import TodoForm from '../components/TodoForm.vue';
import TodoList from '../components/TodoList.vue';
export default {
components: { TodoForm, TodoList },
};
</script>
单文件组件
是时候创建单文件组件的<template>
部分了:
- 在
client/pages
文件夹中,打开index.vue
文件。 - 在
<template>
部分,创建一个div
HTML 元素,作为子元素添加一个section
HTML 元素,class
属性定义为hero is-primary
。然后,作为section
HTML 元素的子元素,创建一个div
HTML 元素,class
属性定义为hero-body
。作为div.hero-body
HTML 元素的子元素,创建一个class
属性定义为container
的div
HTML 元素,并作为子元素添加一个class
属性定义为title
的h1
HTML 元素,内部文本为Todo App
:
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title">
Todo App
</h1>
</div>
</div>
</section>
- 作为
section.hero.is-primary
HTML 元素的同级,创建一个section
HTML 元素,其中class
属性定义为section
,而style
属性定义为padding: 1rem
。将一个class
属性定义为container
的div
HTML 元素作为子元素添加到一个ref
属性定义为list
的子todo-list
组件中:
<section
class="section"
style="padding: 1rem"
>
<div class="container">
<todo-list
ref="list"
/>
</div>
</section>
- 最后,作为
section.section
HTML 元素的同级,创建一个section
HTML 元素,其中class
属性定义为section
,而style
属性定义为padding: 1rem
。添加一个子div
HTML 元素,该元素的class
属性定义为container
,子todo-form
组件的@new-task
事件监听器定义为$refs.list.getTasks()
:
<section
class="section"
style="padding: 1rem"
>
<div class="container">
<todo-form
@new-task="$refs.list.getTasks()"
/>
</div>
</section>
它是如何工作的。。。
此配方显示了通过 Python 的本地 API 服务器与通过Nuxt.js
提供服务的 SSR 平台之间的集成。
当您首先启动 Python 服务器时,您正在打开端口以作为被动客户机从客户机接收数据,只是在等待启动代码时发生一些事情。通过同样的过程,Nuxt.js
SSR 可以在幕后做很多事情,但当它完成时,它就会闲置,等待用户的操作。
当用户与前端交互时,应用可以向服务器发送一些请求,这些请求将与数据一起返回给用户,并显示在屏幕上。
另见
您可以在上了解有关 Flask 和 Python 内部 HTTP 项目的更多信息 https://palletsprojects.com/p/flask/.
如果您想了解更多关于Nuxt.js
的信息,您可以阅读上的文档 https://nuxtjs.org/guide/.
如果您想了解更多有关 Axios 的Nuxt.js
实现以及如何配置和使用插件的信息,您可以阅读上的文档 https://axios.nuxtjs.org/options.
如果你想了解更多关于 Bulma 的信息,这个配方中使用的 CSS 框架,你可以在找到更多信息 https://bulma.io.
Vue 应用的注意事项
安全性一直是每个人都担心的问题,这对技术来说也没有什么不同。你需要时刻保持警觉。在本节中,我们将介绍如何使用一些技术和简单的解决方案来防止攻击。
短绒
使用 ESLint 时,请确保已启用 Vue 插件,并且遵循强烈建议的规则。这些规则将帮助您进行开发,检查一些常见的错误,这些错误可能会打开攻击的大门,例如[T0]指令。
在Vue-CLI
项目中,选择过梁选项后,将与项目文件一起创建一个名为.eslintrc.js
的文件。在此文件中,将预先确定一组基本规则。以下是ESLint + AirBnb
项目的一套良好实践规则示例:
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/essential',
'plugin:vue/recommended',
'plugin:vue/strongly-recommended',
'@vue/airbnb',
],
parserOptions: {
parser: 'babel-eslint',
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
};
现在,如果您有任何违反 lint 规则的代码,它将不会在开发或构建时被解析。
JavaScript
JavaScript 有一些漏洞,可以通过遵循一些简单的检查表和简单的实现来防止。这些实现可以在客户机-服务器通信或 DOM 操作中实现,但您始终需要小心不要忘记它们。
以下是一些使用 JavaScript 的提示:
- 尽可能使用经过身份验证和加密的 API。记住 JWT 本身并不是加密的;您需要添加加密层(JWE)来拥有整个 JSON。
- 如果要存储 API 令牌,请始终使用
SessionStorage
。 - 在将 HTML 输入发送到服务器之前,请始终清理用户输入的 HTML。
- 在将 HTML 呈现到 DOM 之前,始终对其进行清理。
- 始终避开用户的任何
RegeExp
;它将被执行,以防止任何 CPU 线程攻击。 - 始终捕获错误,不向用户显示任何堆栈跟踪,以防止任何代码操纵。
以下是一些使用 JavaScript 时不应做的提示:
- 切勿使用
eval()
;它使代码运行缓慢,并为恶意代码在代码内部执行打开了一扇门。 - 在未对数据进行任何清理或转义的情况下,切勿呈现用户的任何输入。
- 不要在没有任何清理的情况下在 DOM 上呈现任何 HTML。
- 切勿在
LocalStorage
上存储 API 令牌。 - 切勿将敏感数据存储在 JWT 对象中。
视图(&V)
在开发 Vue 应用时,您需要检查一些基本规则,这些规则有助于开发,不会为应用的外部操作打开任何大门。
以下是使用 Vue 的一些提示:
- 始终将类型验证添加到您的道具中,如果可能,还应进行验证程序检查。
- 避免组件的全球注册;使用本地组件。
- 如果可能,请始终使用延迟加载的组件。
- 使用
$refs
代替直接 DOM 操作。
以下是有关使用 Vue 时不应执行的操作的一些提示:
- 切勿在窗口或任何全局范围中存储[T0]、[T1]、[T2]或任何应用变量。
- 切勿修改 Vue 原型;如果需要向原型添加新变量,请创建新的 Vue 插件。
- 不建议在组件之间使用直接连接,因为这样会使组件绑定到父级或子级。
另见
您可以在[T0]的 OWASP CheatCheat 上找到更多关于 XSS(跨站点脚本)的信息 https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.md 和关于的 HTML XSShttps://html5sec.org/.
有关eslint-vue-plugin
的更多信息,请访问https://eslint.vuejs.org/.
有关 Node.js 安全最佳实践的更多信息,请访问https://github.com/i0natan/nodebestpractices#6-安全最佳做法。
有关 Vue 应用的注意事项的更多信息,请访问https://quasar.dev/security/dos-and-donts.****
版权属于:月萌API www.moonapi.com,转载请注明出处