前置

从创建一个简单浏览器导航首页项目展开,该篇随笔包含以下内容的简单上手:

  • 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 全家桶体验的更多相关文章

  1. 助你上手Vue3全家桶之Vue3教程

    目录 前言 1,setup 1.1,返回值 1.2,注意点 1.3,语法 1.4,setup的参数 2,ref 创建响应式数据 3,reactive 创建响应式数据 4,computed 计算属性 5 ...

  2. 助你上手Vue3全家桶之Vue-Router4教程

    目录 1,前言 1,Router 2.1,跳转 2.2,打开新页面 3,Route 4,守卫 4.1,onBeforeRouteLeave 4.2,onBeforeRouteUpdate 4.3,路由 ...

  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 ...

  4. 基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)

    vite 最近非常火,它是 vue 作者尤大神发布前端构建工具,底层基于 Rollup,无论是启动速度还是热加载速度都非常快.vite 随 vue3 正式版一起发布,刚开始的时候与 vue 绑定在一起 ...

  5. vue3 vite2 封装 SVG 图标组件 - 基于 vite 创建 vue3 全家桶项目续篇

    在<基于 vite 创建 vue3 全家桶>一文整合了 Element Plus,并将 Element Plus 中提供的图标进行全局注册,这样可以很方便的延续 Element UI 的风 ...

  6. 开箱即用 yyg-cli(脚手架工具):快速创建 vue3 组件库和vue3 全家桶项目

    1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...

  7. Vue3全家桶升级指南一composition API

    1.setup() vue3中的composition API中最重要的就是setup方法了,相当于组件的入口,所有的composition API都必须放到setup()中的使用. setup是在组 ...

  8. Vue3全家桶升级指南二ref、toRef、toRefs的区别

    ref是对原始数据的拷贝,当修改ref数据时,模板中的视图会发生改变,但是原始数据并不会改变. toRef是对原始数据的引用,修改toRef数据时,原始数据也会发生改变,但是视图并不会更新. 在vue ...

  9. Vue3 全家桶,从 0 到 1 实战项目,新手有福了

    前端发展百花放,一技未熟百技出.未知何处去下手,关注小编胜百书. 我是前端人,专注分享前端内容! 本篇文章主要是,使用 vite 创建一个vue3 项目,实践 vie-router4 vuex4 结合 ...

随机推荐

  1. MyBatis(一) 概述与SQL定制、对象映射

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.MyBatis概述 1.mybatis简介 MyBatis 是支持定制化 SQL.存储过程以及高级映 ...

  2. Java实现 LeetCode 769 最多能完成排序的块(单向遍历)

    769. 最多能完成排序的块 数组arr是[0, 1, -, arr.length - 1]的一种排列,我们将这个数组分割成几个"块",并将这些块分别进行排序.之后再连接起来,使得 ...

  3. Linux文件处理命令 ls 详解

    Linux系统的应用场景最多的就是用作服务器的系统了,简洁,安全,高效,一般我们服务器端不会安装Linux的图形化界面,虽然现在一些Linux发行版的图形界面也很漂亮,但是,服务器最主要的是高效.所以 ...

  4. opencl(7) 内核执行命令入队]工作组、工作项

    1:将内核执行命令入队(该命令可被多个工作项执行) cl_int clEnqueueNDRangeKernel( cl_command_queue command_queue, cl_kernel k ...

  5. chattr +i 用户也没法随意删除

    root用户也没法用rm随意删除文件?   前言 在你的印象中,是不是root用户就可以为所欲为呢?随便一个rm -rf *,一波骚操作走人?可能没那么容易. 先来个示例,创建一个文本文件test.t ...

  6. cacti 流量断图

    问题描述 Cacti监控系统新增了一台设备,后来查询流量的时候发现流量不太对,客户跑的流量远不止8M, 下边就是记录一下问题解决的过程了. 解决过程   看到 rrdtool info 2331.rr ...

  7. Prometheus监控Docker Swarm集群(一)

    Prometheus监控Docker Swarm集群(一) cAdvisor简介 为了解决容器的监控问题,Google开发了一款容器监控工具cAdvisor(Container Advisor),它为 ...

  8. JDK Base64编解码1.7和1.8的坑

    场景 对接一个第三方api接口,其中签名部分用的是JDK8的编码.我们线上采用JDK7,导致项目无法编译 替换编解码部分为1.7的代码,然后签名又不对 所以坑就在这里,结论,1.7的编解码有换行符导致 ...

  9. Javascript 随机显示数组元素

    $(function(){ var wx_arr = ['gb3055','gb6365']; var storage = window.localStorage; function random_w ...

  10. Mysql:bit类型的查询与插入

    原文链接:https://www.cnblogs.com/cuizhf/archive/2013/05/17/3083988.html Mysql关于bit类型的用法: 官方的资料如下: 9.1.5. ...