npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nice-ui-1

v0.0.0

Published

> yarn create vite-app vue3-ui > cd vue3-ui > yarn > yarn dev

Downloads

3

Readme

vue3-ui

框架搭建

创建目录并启动:

yarn create vite-app vue3-ui cd vue3-ui yarn yarn dev

查看 vue-router 版本号

yarn info vue-router versions

安装 vue-router

yarn add [email protected]

找不到模块 xxx.vue

出现原因 TypeScript 只能理解 .ts 文件,无法理解 .vue 文件 解决办法 搜索 Vue3 can not find module 创建 xxx.d.ts,告诉 TS 如何理解 .vue 文件

初始化 vue-router

  • 新建 history 对象
  • 新建 router 对象
  • 引入 TypeScript
  • app.use(router)
  • 添加 < router-view >
  • 添加 < router-line >

创建官网

Home.vue Topnav:左边是 logo,右边是 menu Banner:文字介绍+开始按钮 Doc. Topnav:左边是 logo,右边是 menu Content:左边是 aside,右边是 main

VSCode 插件推荐

Vue 3 Snippets,Auto Import

实现 Switch 组件

API 设计

Switch 组件有以下 4 种使用情况:

<Switch value="true" /> <!-- 字符串 true-->
<Switch value="false" /> <!-- 字符串 false-->
<Switch :value="true" /> <!-- 布尔值 true-->
<Switch :value="false" /> <!-- 布尔值 false-->

即当 value 为字符串 "true" 或布尔值 true 时,显示为开,其他情况显示为关。

Vue3 父子组件通信原理 $event 的值是 emit() 的第二个参数。

Vue3 新版 v-model 用法

要求: 假设属性名为 value,emit() 事件名必须是 "update:value" 效果: <Switch :value="checked" @update:value="checked = $event" /> 可以简写为

实现 Button 组件

API 设计

<Button
  @click=?
  @focus=?
  @mouseover=?
  theme="button or link or text"
  level="main or normal or minor"
  size="big normal small"
  disabled
  loading
></Button>

Vue3 属性绑定

  • 默认所有属性都绑定到根元素
  • 使用 inheritAttrs: false 可以取消默认绑定
  • 使用 $attrs 或者 context.attrs 获取所有属性
  • 使用 v-bind="$attrs" 批量绑定属性
  • 使用 const {size, ...rest} = context.attrs 将属性结构

UI 库的 CSS 注意事项

  1. 不能使用scoped
  2. 样式名称必须加前缀,以区分一般的样式命名,防止被覆盖

实现 Dialog 组件

需求

  1. 点击后弹出
  2. 有遮罩层 overlay
  3. 有 close 按钮
  4. 有标题
  5. 有内容
  6. 有 取消/确定 按钮

API 设计

<Dialog
  visible
  title="标题"
  @confirm="fn1"
  @cancle="fn2"
></Dialog>

新组件 Teleport

为防止 Dialog 被遮挡,可以使用 Teleport 组件,把 Dialog 移到 body 下。

用 ts 文件创建 Dialog 组件

import Dialog from './Dialog.vue'
import { createApp, h } from 'vue'
export const openDialog = (options) => {
  const { title, content, confirm, cancel } = options
  // 创建 div 标签,用于放置 Dialog
  const div = document.createElement('div')
  document.body.appendChild(div)
  // 改变 visible 时,将 Dialog 组件和 div 容器销毁
  const close = () => {
    app.unmount(div)
    div.remove()
  }
  // 创建 Dialog 组件
  const app = createApp({
    render() {
      return h(Dialog, {
        visible: true,
        'onUpdate:visible': (newVisible) => {
          if (newVisible === false) {
            close()
          }
        },
        confirm,
        cancel
      }, {
        title, content
      })
    }
  })
  // 将 Dialog 组件添加到 div
  app.mount(div)
}

实现 Tabs 组件

需求

  1. 点击 Tab 切换内容
  2. Tab 底部横线切换动画

API 设计

<Tabs>
  <Tab title="导航1">内容1</Tab>
  <Tab title="导航2"><Component1 /></Tab>
  <Tab title="导航3"><Component1 x="hi" /></Tab>
</Tabs>
或者
<Tabs :data="[
  {title: '导航1', content: '内容1'},
  {title: '导航2', content: Component1},
  {title: '导航3', content: h(Component1, {x:'hi'})}
]" />

检查 Tabs 组件的子元素是否为 Tab 组件

import Tab from './Tab.vue'
export default {
  setup(props, context) {
    // 可以把 context log 出来看,
    // context.slots.default() 的内容就是外部传进来的子内容
    const defaults = context.slots.default()
    // 对子内容进行类型检查,如果不是 Tab 就报错
    defaults.forEach((tab) => {
      if (tab.type !== Tab) {
        throw new Error('Tabs 子元素必须是 Tab')
      }
    })
    return {
      defaults
    }
  }
}

用 JS 获取插槽内容

const defaults = context.slots.default()

Typescript 泛型

const indicator = ref<HTMLDivElement>(null)

获取宽高和位置

const { width, left } = el.getBoundingClientRect()

小知识:

Vue2 和 Vue3 的区别

90% 的写法完全一致,除了以下几点:

  • Vue3 的 Template 支持多个根标签,Vue2 不支持
  • Vue3 有 createApp(),而 Vue2 的是 new Vue()
  • createApp(组件),new Vue({template, render})
  • 新 v-model 代替以前的 v-model 和 .sync
  • 新增 context.emit,与 this.$emit 作用相同

打包之后预览

yarn build 打包之后,可以通过 http-server 启动服务预览 dist 文件。 安装 http-server

yarn global add http-server 安装之后输入命令: hs dist/ -c-1

消除重复,简化代码

引入 markdown 之后,可以发现三个 vue 文件的代码几乎一样。因此可以新建一个组件代替这三个文件。 消除重复

第 1 次优化:抽象组件

观察代码可知,三个文件的区别是引入的 md 文件不同,将文件路径作为 props 传给组件。

// Markdown.vue
<template>
  <article class="markdown-body" v-html="md">
  </article>
</template>
<script lang="ts">
export default {
  props: {
    path: {
      type: String,
      required: true
    }
  }
}
</script>

然后,可以用异步 import 方法,动态引入内容。

// Markdown.vue
<template>
  <article class="markdown-body" v-html="content">
  </article>
</template>
<script lang="ts">
import { ref } from "vue"
export default {
  props: {
    path: {
      type: String,
      required: true
    }
  },
  setup(props) {
    // content 初始值为 null
    const content = ref<string>(null)
    // 异步 import 文件内容,并将结果赋值给 content
    import(props.path).then(result => {
      content.value = result.default
    })
    return {
      content
    }
  }
}
</script>

第 2 次优化:全局注册组件

经过上述优化后,原本的三个 vue 文件内容如下:

// 以 Intro.vue 为例
<template>
  <Markdown path="../markdown/intro.md" />
</template>
<script lang="ts">
import Markdown from '../components/Markdown.vue'
</script>

为了不必单独引入 Markdown 组件,可以在全局注册组件。

// main.ts
import { createApp } from "vue"
import App from "./App.vue"
import Markdown from './components/Markdown.vue'

const app = create(App)
app.mount("#app")
app.component("Markdown", Markdown)

全局注册组件之后,在 vue 文件中可直接使用。因此上述文件简化为:

// 以 Intro.vue 为例
<template>
  <Markdown path="../markdown/intro.md" />
</template>

第 3 次优化:用 h() 函数代替组件

上述优化之后,文件的代码简洁了,可是三个相似的文件显得有点多余。我们可以在 router.ts 文件中,用 vue 的 h() 函数代替组件。

// router.ts
import { createWebHashHistory, createRouter } from 'vue-router'
import Home from './views/Home.vue'
import Doc from './views/Doc.vue'
import Markdown from './components/Markdown.vue'
import { h } from 'vue'

const history = createWebHashHistory()
export const router = createRouter({
    history,
    routes: [
        { path: '/', component: Home },
        { path: '/doc',
          component: Doc,
          children: [
            { path: '', component: DocDemo },
            // h() 第一个参数传组件名,第二个参数传 props 对象,其中 key 用于区分组件进行视图刷新
            { path: 'intro', component: h(Markdown, { path: '../markdown/intro.md', key: 'intro' }) },
            { path: 'get-started', component: h(Markdown, { path: '../markdown/get-started.md', key: 'get-started' }) },
            { path: 'install', component: h(Markdown, { path: '../markdown/install.md', key: 'install' }) },
          ]
        },
    ]
})

经过这步优化之后,上述三个 vue 文件可以删除了。

第 4 次优化:简化 h() 调用

然而观察上述 h() 的调用,仍然有重复的部分。继续消除重复,将重复的部分提取出来,简化 h() 调用。

// router.ts
...
const history = createWebHashHistory()
// 将重复的部分抽象为 md() 方法
const md = filename => h(Markdown, { path: `../markdown/${filename}.md`, key: filename })
export const router = createRouter({
    history,
    routes: [
        { path: '/', component: Home },
        { path: '/doc',
          component: Doc,
          children: [
            { path: '', component: DocDemo },
            // 调用 md() 方法只要传文件名就可以了
            { path: 'intro', component: md('intro') },
            { path: 'get-started', component: md('get-started') },
            { path: 'install', component: md('install') },
          ]
        },
    ]
})

经过不断的消除重复,我们可以将代码简化到不能再简化。

高亮代码

引用 prismjs 库。

<div class="demo-code">
  <pre class="language-html" v-html="Prism.highlight(SwitchDemo.__sourceCode, Prism.languages.html, 'html')"></pre>
</div>

打包遇到的问题

build 之后不加载 md 文件

这是因为 rollup 不支持动态引入,即不能在 import() 时拼接字符串。

发布的步骤

  • 如果有 dist 目录,则删除 dist 目录
  • 在 .gitignore 添加一行 /dist/ 然后提交代码
  • 运行 yarn build 创建出最新的 dist
  • 运行 hs dist 在本地测试网站是否成功运行
  • 部署到 GitHub
  1. 运行 cd dist
  2. 运行 git init; git add; git commit -m 'init';
  3. 新建远程仓库 nice-website,并关联到 dist 目录
  4. 开启 nice-website 的 Pages 功能

项目展示

niceui

将项目发布到 npm

  1. 在 package.json 里添加 files 和 main:
{
  "name": "nice-ui",
  "version": "0.0.0",
  "files": ["dist/lib/*"],
  "main": "dist/lib/nice.js",
  ...
}
  1. 确保在使用 npm 官方源 在终端输入 npm config get registry,看返回是否是 https://registry.npmjs.org/。 不是的话则用命令 npm config get registry https://registry.npmjs.org/ 更改。
  2. 去 npm 官网注册账号 注册完之后,在终端输入 npm login,然后输入用户名、密码和邮箱进行登录。
  3. 运行 npm publish 发布项目
  4. 用 npm logout 退出登录