QPANCQuasar PostgreSQL ASP NET Core 的首字母。

  • 来源

  • 简介

  • 第一部分 - ASP.NET - 启动项目

  • 第 2 部分 - PostgreSQL

  • 第 3 部分 - ASP.NET - 注册服务和读取环境变量

  • 第 4 部分 - ASP.NET - 实体框架和 ASP.NET 核心标识

  • 第 5 部分 - ASP.NET - 使用 Swagger 的交互式文档

  • 第 6 部分 - ASP.NET - 区域化

  • 第 7 部分 - ASP.NET - 身份验证和授权

  • 第 8 部分 - ASP.NET - CORS

  • 第 9 部分 - Quasar - 项目创建和配置

  • 第 10 部分 - Quasar - 设置和自定义

  • 第 11 部分 - Quasar - 组件 - SPA 和 SSR 之间的区别

  • 第 12 部分 - Quasar - 服务

  • 第 13 部分 - Quasar - 区域化和存储

  • 第 14 部分 - Quasar - 使用 API

  • 第 15 部分 - Quasar - 登录

  • 第 16 部分 - 类星体 - 保护区

  • 第 17 部分 - 类星体 - 记录

  • 第 18 部分 - Docker - Linux 虚拟机

  • 第 19 部分 - Docker - 注册表和构建

  • 第 20 部分 - Docker - Traefik 和发布

  • 在线演示

24 将对象注入到组件、存储和路由中。

为了一个SSR应用程序的正常运行,需要将一些服务以隔离的方式实例化,使得一个用户无法访问发往另一个用户的实例,简而言之,所有的服务都必须在用户范围内进行隔离。

实现这一目标的第一步是创建一个实用程序,将这些服务注入componentsstoreroutes

QPANC.App/src/boot/inject.js

import Vue from 'vue'

const mixins = []
const inject = function (bootCb) {
  return async function (ctx) {
    const { app, router, store } = ctx
    let boot
    if (typeof bootCb === 'function') {
      const response = bootCb(ctx)
      boot = response.then ? await response : response
    } else {
      boot = bootCb
    }

    for (const name in boot) {
      const key = `$${name}`
      if (mixins.indexOf(name) === -1) {
        mixins.push(name)
        Vue.mixin({
          beforeCreate () {
            const options = this.$options
            if (options[name]) {
              this[key] = options[name]
            } else if (options.parent) {
              this[key] = options.parent[key]
            }
          }
        })
      }
      app[name] = boot[name]
      store[key] = boot[name]
      router[key] = boot[name]
    }
  }
}

export default inject

现在,一个使用示例:

假设我们有一个在控制台上打印消息Hello World的服务:

class DummieService {
  sayHello ({ name }) {
    console.log(`${name}: Hello Wolrd`)
  }
}

现在我们想将上述服务的一个实例注入到所有componentesstoresroutes中,我们可以在引导文件中进行。

import inject from './inject'
import DummieService from 'services/dummie'

export default inject(({ Vue }) => {
  const dummie = new DummieService()
  return {
    dummie: dummie
  }
})

完成后,我们可以在componentesstores上访问this.$dummie,以及在导航守卫上访问router.$dummie,以下是一些示例:

示例/页面/dummy.js

export default {
  data () {
    const initialMsg = this.$dummie.sayHello({ name: 'me' })
    return {
      msg: initialMsg
    }
  },
  methods: {
    changeName ({ name }) {
      this.msg = this.$dummie.sayHello({ name })
    }
  }
}

示例/商店/dummie.js

export default {
  namespaed: true,
  state () {
    return {
      name: 'me',
      msg: ''
    }
  },
  mutations: {
    name (state, value) { state.name = value },
    msg (state, value) { state.msg = value }
  },
  actions: {
    setMessage ({ commit }, name) {
      const msg = this.$dummie.sayHello({ name })
      commit('msg', msg)
    }
  },
  getters: {
    getMessage (state) {
      return this.$dummie.sayHello({ name: state.name })
    }
  }
}

示例/路由器/dummy.js

export default function (context) {
  return {
    path: '/dummie',
    beforeEnter (to, from, next) {
      const { $dummie } = context.router
      const msg = $dummie.sayHello({ name: to.params.name })
      if (msg.length > 50) {
        next('/hello-long-name')
      } else {
        next('/hello-short-name')
      }
    }
  }
}

25 自定义 Quasar 组件

对于这个任务,我将使用@toby-mosque/utils扩展,但我不会在不使用扩展的情况下显示等效代码,因为它涉及一个非常复杂的transparent wrapper,其中一个小错误可能会导致难以追踪的错误或不需要的行为。

对于这个演示,我们将只定制QInput,但我们可以定制任何组件,包括那些通过扩展安装的组件。

第一步是创建一个boot,这里我们称之为brand

quasar new boot brand

QPANC.App/quasar.config.js

module.exports = function (ctx) {
  return {
    boots: [
      'brand'
    ]
  }
}

QPANC.App/src/boot/inject.js

import { factory } from '@toby.mosque/utils'
import inject from './inject'
import { QInput } from 'quasar'

// "async" is optional
export default inject(({ Vue }) => {
  const brand = {}
  brand.input = Vue.observable({
    /*
    style: {
      'font-size': '12px'
    },
    class: {
      'custom-input': true
    },
    */
    props: {
      outlined: true
    }
  })

  factory.reBrand('q-input', QInput, brand.input)
  return {
    brand
  }
})

对象brand将被注入到componentesstores中,所以我们以后可以访问它。

input属性是Vue.observable,对它的任何更改都会反映到使用它的组件上。input只是一个名字,它可以是任何东西。

factory.reBrand负责将brand.input注入所有q-input。它只会使用属性styleclassprops,其中这些属性将被注入到相应组件的styleclassprops中,这些属性都不是强制性的。

如果您现在运行应用程序,您将看到所有输入都将具有属性:outlined="true"

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--bkleUz4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/n7aeea8sam0tar3o6euu.png)

现在,让我们修改上面的例子,使用Dark Mode,在dark模式下输入应该是filled,在light模式下输入应该是outlined

QPANC.App/src/boot/inject.js

import { factory } from '@toby.mosque/utils'
import inject from './inject'
import { QInput, Dark } from 'quasar'

// "async" is optional
export default inject(({ Vue }) => {
  const brand = {}
  brand.input = Vue.observable({
    /*
    style: {
      'font-size': '12px'
    },
    class: {
      'custom-input': true
    },
    */
    props: {
      filled: Dark.isActive
      outlined: !Dark.isActive
    }
  })

  factory.reBrand('q-input', QInput, brand.input)
  return {
    brand
  }
})

这里的问题是Dark.isActive仅用作brand.input的初始值,但是当Dark.isActive更改时,它不会传播到brand.input

所以我们在App.vue中需要一个watch

QPANC.App/src/App.vue

export default {
  name: 'App',
  watch: {
    '$q.dark.isActive' () {
      this.$brand.input.props.filled = this.$q.dark.isActive
      this.$brand.input.props.outlined = !this.$q.dark.isActive
    }
  }
}

最后,一些印刷品:

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--q_85bLxG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/f02bpj2wrvsrav1g4dsk.png)

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--ubttai1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/xukf5vxur4h2y3s85haq.png)

25 Cookie 持久性

由于这是一个SSR应用程序,一些数据会自然地保存在客户端,例如Token JWT、主题和首选语言。

但是,其中一些数据需要在服务器端访问,否则无法检索,我们将不得不使用 Cookie。

第一步是激活负责读取 Cookie 的插件。

QPANC.App/quasar.config.js

module.exports = function (ctx) {
  return {
    framework: 
      plugins: [
        'Cookies'
      ]
    }
  }
}

现在,我们将为 vuex 添加一个插件,在本例中为vuex-persistedstate

yarn add vuex-persistedstate

现在添加引导persist,不要让它添加到quasar.config.js>boots,在引导axiosi18n之前添加它是至关重要的。

quasar new boot persist

QPANC.App/quasar.config.js

module.exports = function (ctx) {
  return {
    boots: [
      'persist',
      'i18n',
      'axios'
    ]
  }
}

在编码 bootpersist之前,我们需要创建一个小服务,该服务将负责为用户检测推荐的语言。

QPANC.App/src/services/locales.js

const locales = ['en-us', 'pt-br']
const regions = {
  en: 'en-us',
  pt: 'pt-br'
}
const fallback = regions.en
const detectLocale = function () {
  if (process.env.CLIENT) {
    const locale = navigator.language.toLowerCase()
    if (locales.includes(locale)) {
      return locale
    }
    const region = locale.split('-')[0]
    if (region in regions) {
      return regions[region]
    }
    return regions.en
  } else {
    return fallback
  }
}

export {
  locales,
  regions,
  fallback,
  detectLocale
}

现在,让我们开始吧:

QPANC.App/src/boot/persist.js

import { Cookies, Quasar } from 'quasar'
import createPersistedState from 'vuex-persistedstate'

const persistState = function ({ name, store, storage }) {
  createPersistedState({
    key: name,
    paths: [name],
    filter ({ type }) {
      return type.startsWith(name)
    },
    storage
  })(store)
}

export default function ({ store, ssrContext }) {
  const cookies = process.env.SERVER
    ? Cookies.parseSSR(ssrContext)
    : Cookies

  const cookieStorage = {
    getItem (key) {
      return JSON.stringify(cookies.get(key))
    },
    setItem (key, value) {
      cookies.set(key, value, { path: '/' })
    },
    removeItem (key) {
      cookies.remove(key, { path: '/' })
    }
  }

  persistState({ name: 'app', store, storage: cookieStorage })
  if (process.env.CLIENT) {
    // persistState({ name: 'local', store, storage: window.localStorage })
    store.commit('app/localeOs', detectLocale())
  }
}

关于正在做的事情的一些细节:

persistState({ name: 'app', store, storage: cookieStorage })

我们指示vuex-persistedstate将整个app模块保存在Cookie

if (process.env.CLIENT) {
  // persistState({ name: 'local', store, storage: window.localStorage })
}

如果注释被删除,我们是在指示vuex-persistedstate将整个local模块持久化到localStorage

if (process.env.CLIENT) {
  store.commit('app/localeOs', detectLocale())
}

我们正在更新localeOs,使其等于可用的locale,最接近浏览器使用的语言,这将是应用程序语言,以防用户也未指定localeUser

但是,需要记住的是,客户端的代码是在服务器上执行之后才执行的,因此,在第一次请求中,服务器将始终使用默认语言,在这种情况下为英语(可在quasar.config.js > framework > lang中配置)。

这样,在第一次请求时,应用程序将使用默认语言加载,并在页面加载后切换到浏览器通知的语言。

现在,让我们创建我们的模块app

QPANC.App/src/store/app.js

import { factory } from '@toby.mosque/utils'

class AppStoreModel {
  constructor ({
    token = '',
    localeOs = '',
    localeUser = ''
  } = {}) {
    this.token = token
    this.localeOs = localeOs
    this.localeUser = localeUser
  }
}

const options = {
  model: AppStoreModel
}

export default factory.store({
  options,
  getters: {
    locale (state) {
      return state.localeUser || state.localeOs
    }
  }
})

export { options, AppStoreModel }

由于这是一个全局模块,请不要忘记在QPANC.App/src/store/index.js上注册它

QPANC.App/src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import app from './app'

Vue.use(Vuex)

export default function (context) {
  const Store = new Vuex.Store({
    modules: {
      app
    },
    strict: process.env.DEV
  })

  return Store
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐