⚡ vue3 全家桶体验
前置
从创建一个简单浏览器导航首页项目展开,该篇随笔包含以下内容的简单上手:
- vite
- vue3
- vuex4
- vue-router next
预览效果有助于理清这些内容,限于篇幅,不容易展开叙述。由于项目逻辑简单,只使用了少量 API,我只是写这个小项目过把手瘾,所以对应标题 上手。如果您只是想学习 vue 周边的 API,那么,这篇文章将给您带来有限的知识。
初始化项目
使用 vite 初始化 vue3 项目。什么是 vite?Vite 是一个 Web 构建工具。开发过程中通过浏览器 ES Module 导入为您的代码提供服务,生成环境与 Rollup 捆绑在一起进行打包。
特性:
- 闪电般快速的冷服务器启
- 动即时热模块更换(HMR)
- 真正的按需编译
vite 截至今天支持的功能:
- Bare Module Resolving
- Hot Module Replacement
- TypeScript
- CSS / JSON Importing
- Asset URL Handling
- PostCSS
- CSS Modules
- CSS Pre-processors
- JSX
- Web Assembly
- Inline Web Workers
- Custom Blocks
- Config File
- HTTPS/2
- Dev Server Proxy
- Production Build
- Modes and Environment Variables
npm init vite-app aweshome
npm install
npm run dev
npm run build
最终生成的目录结构与使用 vue-cli 相似:
│ .npmignore
│ a.txt
│ index.html
│ package.json
├─public
│ favicon.ico
└─src
│ App.vue
│ index.css
│ main.js
├─assets
│ logo.png
└─components
HelloWorld.vue
可以在项目根目录下创建 vite.config.js 配置 Vite:
module.exports = {
// 导入别名
// 这些条目可以是精确的请求->请求映射*(精确,无通配符语法)
// 也可以是请求路径-> fs目录映射。 *使用目录映射时
// 键**必须以斜杠开头和结尾**
alias: {
// 'react': '@pika/react',
// 'react-dom': '@pika/react-dom'
// '/@foo/': path.resolve(__dirname, 'some-special-dir'),
},
// 配置Dep优化行为
optimizeDeps: {
// exclude: ['dep-a', 'dep-b'],
},
// 转换Vue自定义块的功能。
vueCustomBlockTransforms: {
// i18n: src => `export default Comp => { ... }`,
},
// 为开发服务器配置自定义代理规则。
proxy: {
// proxy: {
// '/foo': 'http://localhost:4567/foo',
// '/api': {
// target: 'http://jsonplaceholder.typicode.com',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, ''),
// },
// },
},
// ...
}
更多配置可以参考Github。
另外,现在可以使用 vitepress 代替原来的 vuepress 构建文档或博客。
vue-router next
npm i vue-router@next
src/router/index.js
import {createRouter, createWebHistory} from 'vue-router'
import Home from '../components/home/Home.vue'
import Cards from '../components/cards/Cards.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
// route -> routes
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/cards',
name: 'cards',
component: Cards,
},
],
})
export default router
vue router next 还添加了动态路由,解决规则冲突的问题。做过权限管理应该深有体会。更多配置可以参考 Github。
vuex4
使用与 vuex3 相同的 API。
安装
npm i vuex@next
src/constants 下存放了静态数据,它们都是如下形式:
export const vue = [
{
title: 'vue',
desc: 'Vue 是用于构建用户界面的渐进式的框架',
link: 'https://cn.vuejs.org/v2/guide/',
img: import('../assets/images/vue.png'), // require -> import
},
{
title: 'vue Router',
desc: 'Vue Router 是 Vue.js 官方的路由管理器。',
link: 'https://router.vuejs.org/zh/',
img: import('../assets/images/vue.png'),
},
// ...
]
src/store/index.js
import {createStore} from 'vuex'
import {vue, react, wechat, across, compileBuild} from '../constants/docs'
import {frontEndTools, OfficeTools} from '../constants/tools'
import {tools, docs, community} from '../constants/asideData'
import {blogs} from '../constants/community'
const store = createStore({
state: {
asideData: [],
mainData: [],
},
mutations: {
setAsideData(state, key) {
const asideActions = {
'2': tools,
'3': docs,
'4': community,
}
state.asideData = asideActions[key]
},
setMainData(state, menuItemText) {
const actions = new Map([
['前端工具', frontEndTools],
['办公工具', OfficeTools],
['vue', vue],
['react', react],
['微信开发', wechat],
['跨端框架', across],
['编译构建', compileBuild],
['博客', blogs],
])
state.mainData = actions.get(menuItemText)
},
},
actions: {},
modules: {},
})
export default store
main.js
结合上文的 vuex vue-router 可以看出,vue3 核心插件的 api 都做了简化。
import './index.css'
import {createApp} from 'vue'
import store from './store'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(store)
app.use(router)
app.mount('#app')
sass
npm i sass
package.json > dependencies
{
"dependencies": {
"vue": "^3.0.0-beta.15",
"vue-router": "^4.0.0-alpha.13",
"vuex": "^4.0.0-beta.2"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.0.0-beta.15",
"sass": "^1.26.8",
"vite": "^1.0.0-beta.1"
}
}
components
这个小项目本质上可以只有一个页面 .vue 构成,我将它拆分,便于阅读。
App.vue
<template>
<Header />
<main>
<router-view></router-view>
</main>
<Footer />
</template>
<script>
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
export default {
name: 'app',
components: {
Header,
Footer,
},
}
</script>
<style>
main {
flex: 1;
}
</style>
components/cards/Aside.vue
<template>
<aside>
<ul>
<li :index="item.index" v-for="item in this.$store.state.asideData" :key="item.index" ref="menuItem" @click="handleSelect(item.value)">
<i class="fas fa-home"></i>
<span>{{ item.value }}</span>
</li>
</ul>
</aside>
</template>
<script>
import store from '../../store'
export default {
setup(props, context) {
return {
handleSelect(value) {
store.commit('setMainData', value)
},
}
},
}
</script>
<style lang="scss">
aside {
flex: 1;
background-color: rgb(238, 238, 238);
height: 100%;
li {
display: flex;
align-items: center;
height: 56px;
line-height: 56px;
font-size: 14px;
color: #303133;
padding: 0 1.4rem;
list-style: none;
cursor: pointer;
transition: border-color 0.3s, background-color 0.3s, color 0.3s;
white-space: nowrap;
&:hover {
background-color: rgb(224, 224, 224);
}
}
}
@media screen and (max-width: 768px) {
aside {
display: none;
&.active {
display: block;
}
}
}
</style>
components/cards/Cards.vue
<template>
<div id="card-outer">
<Aside />
<section></section>
</div>
</template>
<script>
import Aside from './Aside.vue'
import router from '../../router'
export default {
components: {
Aside,
},
}
</script>
<style lang="scss">
#card-outer {
display: flex;
align-content: stretch;
height: 100%;
& > section {
flex: 8;
}
}
.main-card {
margin: 10px 0;
cursor: pointer;
.main-card-content {
display: flex;
align-items: center;
img {
width: 30px;
height: 30px;
margin-right: 10px;
}
.main-card-content-info {
width: 90%;
h3 {
font-size: 14px;
}
p {
font-size: 12px;
color: #888ea2;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
line-height: 1.8;
}
}
span {
margin-left: 10px;
text-decoration: none;
&:nth-of-type(1) {
font-size: 18px;
font-weight: 700;
color: #ffa502;
white-space: nowrap;
}
&:nth-of-type(2) {
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
</style>
components/home/Home.vue
<template>
<section id="search">
<div class="search-sources" style="margin-bottom: 10px;">
<span size="mini" type="primary" v-for="(item, index) in source" @click="changeSource(item.name)" :key="index" :style="`background:${item.color};border-color:${item.color}`"
>{{ item.name }}
</span>
</div>
<div class="searchbox" :class="searchbarStyle.className">
<input :placeholder="searchbarStyle.placeholder" v-model="searchValue" clearable v-on:keyup.enter="submit" />
<button @click="submit" slot="append" icon="el-icon-search">
<i class="fas fa-search"></i>
</button>
</div>
</section>
</template>
<script>
export default {
data: () => ({
baseUrl: 'https://www.baidu.com/s?ie=UTF-8&wd=',
searchValue: '',
searchbarStyle: {
className: 'baidu',
placeholder: '百度一下,你就知道',
},
source: [
{
name: '百度',
color: '#2932E1',
},
{
name: '搜狗',
color: '#FF6F17',
},
{
name: 'Bing',
color: '#0c8484',
},
{
name: 'Google',
color: '#4285F4',
},
{
name: 'NPM',
color: '#EA4335',
},
],
}),
methods: { // 可以在 vue3 中使用 options API
changeSource(name) {
const actions = new Map([
[
'百度',
() => {
this.baseUrl = 'https://www.baidu.com/s?ie=UTF-8&wd='
this.searchbarStyle = {
className: 'baidu',
placeholder: '百度一下,你就知道',
}
},
],
[
'Bing',
() => {
this.baseUrl = 'https://cn.bing.com/search?FORM=BESBTB&q='
this.searchbarStyle = {
className: 'bing',
placeholder: '必应搜索',
}
},
],
[
'搜狗',
() => {
this.baseUrl = 'https://www.sogou.com/web?query='
this.searchbarStyle = {
className: 'sougou',
placeholder: '搜狗搜索',
}
},
],
[
'Google',
() => {
this.baseUrl = 'https://www.google.com/search?q='
this.searchbarStyle = {
className: 'google',
placeholder: 'Google Search',
}
},
],
[
'NPM',
() => {
this.baseUrl = 'https://www.npmjs.com/search?q='
this.searchbarStyle = {
className: 'npm',
placeholder: 'Search Packages',
}
},
],
])
actions.get(name)()
},
submit() {
const url = this.baseUrl + this.searchValue
window.open(url)
},
},
}
</script>
<style lang="scss">
#search {
display: flex;
flex-direction: column;
justify-content: center;
align-content: stretch;
margin: 0 auto;
height: 40vh;
width: 40%;
& > div {
display: flex;
}
}
.search-sources {
span {
margin-right: 0.5rem;
padding: 0.4rem 0.6rem;
color: #fff;
font-size: 14px;
line-height: 14px;
border-radius: 2px;
&:hover {
filter: contrast(80%);
transition: 0.3s;
}
}
}
.searchbox {
padding-left: 1rem;
height: 2.6rem;
border-radius: 6px;
background-color: #fff;
border: 1px #ccc solid;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
input {
flex: 7;
border: none;
font-size: 1rem;
}
button {
flex: 1;
i {
margin-right: 0;
}
}
}
$sources-color: (
baidu: #2932e1,
bing: #0c8484,
sougou: #ff6f17,
google: #4285f4,
npm: #ea4335,
);
$source-list: baidu bing sougou google npm;
@each $source in $source-list {
.#{$source} {
&:hover {
border-color: map-get($sources-color, $source);
box-shadow: 0 2px 4px map-get($sources-color, $source);
transition: all 0.5s;
}
input {
&:hover {
border-color: map-get($sources-color, $source);
}
}
}
}
@media screen and (max-width: 768px) {
#search {
width: 90%;
}
}
</style>
components/Header.vue
<template>
<header>
<ul class="nav">
<li @click="handleSelect('home')">
<i class="fas fa-home"></i>
<span>首页</span>
</li>
<li @click="handleSelect('tools')">
<i class="fas fa-tools"></i>
<span>工具</span>
</li>
<li @click="handleSelect('docs')">
<i class="fas fa-file-alt"></i>
<span>文档</span>
</li>
<li @click="handleSelect('community')">
<i class="fas fa-comment-dots"></i>
<span>社区</span>
</li>
</ul>
<MobileMenu />
</header>
</template>
<script>
import MobileMenu from './MobileMenu.vue'
import store from '../store'
import router from '../router'
export default {
components: {
MobileMenu,
},
setup() {
const handleSelect = item => {
store.commit('setAsideData', item)
if (item === 'home') {
router.replace({name: 'home'})
} else {
const actions = {
tools: ['setMainData', '前端工具'],
docs: ['setMainData', 'vue'],
community: ['setMainData', '博客'],
}
store.commit(actions[item][0], actions[item][1])
router.replace({name: 'cards'})
}
}
return {
handleSelect,
}
},
}
</script>
<style lang="scss">
header {
display: flex;
height: 60px;
align-content: stretch;
padding: 0 9.5rem;
}
.nav {
display: flex;
align-items: center;
align-content: stretch;
li {
padding: 0.5rem 0.75rem;
&:hover {
background-color: #f3f1f1;
& span {
color: #3273dc;
}
}
}
}
@media screen and (max-width: 768px) {
header {
padding: 0;
}
}
</style>
components/MobileMenu.vue
<template>
<section id="mobile-menu">
<div id="navbarBurger" class="navbar-burger burger" data-target="navMenuMore" :class="{active}" @click="sideToggle">
<span></span>
<span></span>
<span></span>
</div>
</section>
</template>
<script>
export default {
data: () => ({
active: false,
}),
methods: {
sideToggle() {
this.active = !this.active
const classList = document.querySelectorAll('aside')[0].classList
this.active ? classList.add('active') : classList.remove('active')
},
},
}
</script>
<style lang="scss">
#mobile-menu {
display: none;
position: absolute;
right: 0;
top: 0;
z-index: 999999;
}
@media screen and (max-width: 768px) {
#mobile-menu {
display: block;
.navbar-burger {
position: relative;
color: #835656;
cursor: pointer;
height: 60px;
width: 60px;
margin-left: auto;
span {
background-color: #333;
display: block;
height: 1px;
left: calc(50% - 8px);
position: absolute;
transform-origin: center;
transition-duration: 86ms;
transition-property: background-color, opacity, transform;
transition-timing-function: ease-out;
width: 16px;
&:nth-child(1) {
top: calc(50% - 6px);
}
&:nth-child(2) {
top: calc(50% - 1px);
}
&:nth-child(3) {
top: calc(50% + 4px);
}
}
&.active {
span {
&:nth-child(1) {
transform: translateY(5px) rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:nth-child(3) {
transform: translateY(-5px) rotate(-45deg);
}
}
}
}
}
}
</style>
最后
一套流程下来,vite 给我的感觉就是“快”。对于 vue 周边, API 都是做了一些简化,如果你对 esm 有些了解,将更有利于组织项目,可读性相比 vue2.x 也更高。也有一些问题,限于篇幅,本文没有探讨。做项目还是上 vue2.x 及其周边。另外,我没找到 vue3 组件库。
⚡ vue3 全家桶体验的更多相关文章
- 助你上手Vue3全家桶之Vue3教程
目录 前言 1,setup 1.1,返回值 1.2,注意点 1.3,语法 1.4,setup的参数 2,ref 创建响应式数据 3,reactive 创建响应式数据 4,computed 计算属性 5 ...
- 助你上手Vue3全家桶之Vue-Router4教程
目录 1,前言 1,Router 2.1,跳转 2.2,打开新页面 3,Route 4,守卫 4.1,onBeforeRouteLeave 4.2,onBeforeRouteUpdate 4.3,路由 ...
- 助你上手Vue3全家桶之VueX4教程
目录 1,前言 2,State 2.1,直接使用 2.2,结合computed 3,Getter 3.1,直接使用 3.2,结合computed 4,Mutation 4.1,直接使用 4.2,结合c ...
- 基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)
vite 最近非常火,它是 vue 作者尤大神发布前端构建工具,底层基于 Rollup,无论是启动速度还是热加载速度都非常快.vite 随 vue3 正式版一起发布,刚开始的时候与 vue 绑定在一起 ...
- vue3 vite2 封装 SVG 图标组件 - 基于 vite 创建 vue3 全家桶项目续篇
在<基于 vite 创建 vue3 全家桶>一文整合了 Element Plus,并将 Element Plus 中提供的图标进行全局注册,这样可以很方便的延续 Element UI 的风 ...
- 开箱即用 yyg-cli(脚手架工具):快速创建 vue3 组件库和vue3 全家桶项目
1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...
- Vue3全家桶升级指南一composition API
1.setup() vue3中的composition API中最重要的就是setup方法了,相当于组件的入口,所有的composition API都必须放到setup()中的使用. setup是在组 ...
- Vue3全家桶升级指南二ref、toRef、toRefs的区别
ref是对原始数据的拷贝,当修改ref数据时,模板中的视图会发生改变,但是原始数据并不会改变. toRef是对原始数据的引用,修改toRef数据时,原始数据也会发生改变,但是视图并不会更新. 在vue ...
- Vue3 全家桶,从 0 到 1 实战项目,新手有福了
前端发展百花放,一技未熟百技出.未知何处去下手,关注小编胜百书. 我是前端人,专注分享前端内容! 本篇文章主要是,使用 vite 创建一个vue3 项目,实践 vie-router4 vuex4 结合 ...
随机推荐
- PETS渗透测试标准总结
国外的标准框架,感觉大部分渗透公司的测试指南都是从这俩借鉴的,正好复习下. 国外渗透测试标准:http://www.pentest-standard.org 渗透测试分为:前期交互,情报搜集,威胁建模 ...
- NodeJS及路由
1.基本介绍- http://nodejs.cn/api/ Node.js 是一个基于Chrome V8 引擎的JavaScript运行环境 Node.js使用了一个事件驱动.非阻塞式I/O的模型,使 ...
- 三、TCP协议
TCP(Transmission Control Protocol)传输控制协议:顾名思义就是对数据的传输进行控制 TCP报头 序号:相当于编号,当TCP数据包过大的时候会进行分段,分段之后按序号顺序 ...
- DBusConnection for c
dbus的C API D-Bus 1.13.10 目录 dbus的C API Detailed Description Typedef Documentation ◆ DBusAddTimeoutFu ...
- 如何通过PR给视频添加字幕?
第一步:将视频通过导出音频格式MP3 第二步:将音频MP3导入网易见外平台 第三步:在网易见外平台创建项目,进行语音转写如下所示: 第四步:将从网易见外平台到处的srt字母文件,打开后进行编码为utf ...
- 【福利】FL Studio 20 汉化补丁包 _FL Studio 20 汉化包下载
我这两天在网上搜索FL Studio 20汉化包,找了半天也没有找到真正的汉化包,不过好在功夫不负有心人,让我找到了一个不错的FL Studio 20汉化网站,里面提供了FL Studio 20汉化包 ...
- Bash知识点记录
变量的设置规则 1. 等号两边不能直接接空格符. 2. 右侧的变量内容若有空格符,可使用双引号或单引号将变量内容括起来,其中, 双引号内的特殊字符如 $ 等,可以保有原本的特性.如下所示: ...
- php5.5下安装pdflib的步骤
php5.5下安装pdflib的步骤 1. 下载pdflib 下载地址为:http://www.pdflib.com/download/pdflib-family/pdflib/ 然后选择对应的版本, ...
- Linux下自己和自己用各种方法进行文件的上传下载
环境: Ubuntu 16.04 1.SCP # 上传 scp /home/sea/Desktop/test.sh sea@192.168.1.31:/home/sea/Desktop/test.sh ...
- 怎样在LaTeX中使用中文
因为疫情在家中上课,作业提交都必须使用PDF.反正时间充裕,不如趁机回顾一下LaTeX的使用. 之前一直用的是Vimtex,但是感觉还是不太方便,于是改用了Texpad.Texpad的强大之处在于它支 ...