vue demi支持sfc方式的vue2vue3通用库开发详解

目录
  • 背景
  • 技术要点
    • vue-demi
    • sfc compiler
  • 实现方式
    • vue2.6 + vue3 + vite + vue-demi
    • package.json
    • vite.config.ts
    • main.ts
    • postinstall
    • vue2.7 + vue3 + vite + vue-demi + yarn workspaces
    • 目前没找到vue3为主包的开发方式
  • 注意点
    • 1、@vue/composition-api重复引用问题
    • 2、由于要兼容vue2,vue3的 setup sfc语法糖不兼容
  • 最后

背景

随着vue3的逐渐成熟,公司项目逐渐会存在vue2和vue3项目共存的情况,兼容vue2和vue3的公共组件开发能让老项目较好地过渡到vue3。研究了vue-demi的源码和demo,发现vue-demi只是简单地根据vue版本生成对应的类似中间件的东西,而且render函数也只是做了简单的中转处理;

国外大佬写了一个h-demi解决了vue2/vue3的render函数attrs属性的问题,这里我就直接贴issue链接,不做过多说明了: github.com/vueuse/vue-…

虽然vue-demi没有提供sfc的兼容方案,但是其实仔细想一下,sfc的解析处理也不应该是由vue-demi来解决,应该是交给打包工具将template转成render,而vue-demi只需要关注composition-api就行;于是往着这个思路,花了几天时间研究一下vue2.6、vue2.7和vue3的sfc-compiler,得到以下开发方案。

技术要点

vue-demi

查看源码可以发现,vue-demi的工作是通过postinstall和 npx vue-demi-fix指令,判断当前项目安装的vue版本,然后将对应版本的插件复制到lib的根目录,其插件的功能就是抹平vue2和vue3版本使用composition-api时的差异;

<=2.6: exports from vue + @vue/composition-api with plugin auto installing.

2.7: exports from vue (Composition API is built-in in Vue 2.7).

>=3.0: exports from vue, with polyfill of Vue 2's set and del API.

sfc compiler

在日常开发中写的vue template,实际上最后是通过sfc-compiler转成render函数输出的,而vue2和vue3的sfc-compiler是互不兼容的。尤大大已经提供了vue2.6.x,vue2.7和vue3的compiler,其实我们只需要在打包工具写判断不同的vue版本使用不同的compiler逻辑即可,本文是基于vite开发,以下对应的打包插件:

  • vue2.6: vite-plugin-vue2@2.6.14 + vue-template-compiler@2.6.14
  • vue2.7: vite-plugin-vue2@2.7.9 + vue-template-compiler@2.7.9; 或者@vitejs/plugin-vue2 + @vue/compiler-sfc
  • vue3: @vitejs/plugin-vue + @vue/compiler-sfc

实现方式

以下实现方式均是基于vite开发,换成webpack和rollup原理上也是替换对应的插件即可。

vue2.6 + vue3 + vite + vue-demi

以vue2.6为主包,开发vue2/vue3组件,该方式能做到通过一个package.json的scripts同时调试和打包vue2、vue3环境,以下讲一下重点;

package.json

package.json中的vue包是固定了2.6.14版本,这里要注意vue-template-compiler要和vue的版本对齐;

scripts中的switch:2 指令没有按照文档说的使用npx vue-demi-switch,是因为在实际调试过程中,由于vite是会缓存依赖的,dev调试时vue-demi-switch会出现一些莫名其妙的问题,具体原因我还没搞明白,所以就改成用npx vue-demi-fix。

//package.json部分片段
 "main": "./lib/vue-demi-sfc-component.umd.cjs",
 "exports": {
    ".": {
      "import": "./lib/vue-demi-sfc-component.js",
      "require": "./lib/vue-demi-sfc-component.umd.cjs"
    }
 },
 "scripts": {
    "postinstall": "node ./scripts/postinstall.mjs",
    "dev": "vite",
    "dev:3": "npm run switch:3 && vite --force",
    "dev:2": "npm run switch:2 && vite",
    "switch:2": "npx vue-demi-fix",
    "switch:3": "npx vue-demi-switch 3 vue3",
    "build:3": "npm run switch:3 && vue-tsc --noEmit && vite build",
    "build:2": "npm run switch:2 && vue-tsc --noEmit && vite build",
    "build": "rimraf lib && npm run build:2 && npm run build:3",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
  },
  "dependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue-demi": "^0.13.8"
  },
  "peerDependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue": "^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional": true
    }
  },
  "peerDependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue": "^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional": true
    }
  },
  "devDependencies": {
    // ...其他依赖,这里就不复制了
    "@vitejs/plugin-vue": "^3.0.3",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "2.6.14",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.6.14",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.6.14",
    "vue3": "npm:vue@^3.2.36"
  }

vite.config.ts

import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
import * as compiler from '@vue/compiler-sfc'
import vue3 from '@vitejs/plugin-vue'
import path from 'path'
import { getLibDir } from './scripts/utils.mjs'
import { isVue2, version } from 'vue-demi'
console.log({ version })
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': resolve('src'),
      vue: isVue2 ? resolve('/node_modules/vue2') : resolve('/node_modules/vue3')
    }
  },
  build: {
    lib: {
      entry: resolve('./src/components/index.ts'),
      name: 'vueDemiSfcComponent',
      fileName: 'vue-demi-sfc-component'
    },
    cssTarget: 'chrome61',
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: getLibDir(version),
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [
    isVue2
      ? createVuePlugin()
      : vue3({
          compiler: compiler
        })
  ]
})

这个文件有几个关键逻辑:

1、使用vue-demi的isVue2来判断当前打包环境

import { isVue2, version } from 'vue-demi'

2、alias要根据环境切换地址

 alias: {
  '@': resolve('src'),
  vue: isVue2 ? resolve('/node_modules/vue2') : resolve('/node_modules/vue3')
}

3、在以vue2.6为主包的时候,如果直接使用@vitejs/plugin-vue, 打包时会报错

error when starting dev server:

Error: Failed to resolve vue/compiler-sfc.

@vitejs/plugin-vue requires vue (>=3.2.25) to be present in the dependency tree.

这是因为@vitejs/plugin-vue源码中是直接找vue/compiler-sfc目录的,如果以vue2为主包,这个时候nod_modules/vue是vue2的目录结构,并没有vue/compiler-sfc;

function resolveCompiler(root) {
  const compiler = tryRequire("vue/compiler-sfc", root) || tryRequire("vue/compiler-sfc");
  if (!compiler) {
    throw new Error(
      `Failed to resolve vue/compiler-sfc.
@vitejs/plugin-vue requires vue (>=3.2.25) to be present in the dependency tree.`
    );
  }
  return compiler;
}

所以就去寻找一下@vitejs/plugin-vue的options

interface Options {
    include?: string | RegExp | (string | RegExp)[];
    exclude?: string | RegExp | (string | RegExp)[];
    isProduction?: boolean;
    script?: Partial<Pick<SFCScriptCompileOptions, 'babelParserPlugins'>>;
    template?: Partial<Pick<SFCTemplateCompileOptions, 'compiler' | 'compilerOptions' | 'preprocessOptions' | 'preprocessCustomRequire' | 'transformAssetUrls'>>;
    style?: Partial<Pick<SFCStyleCompileOptions, 'trim'>>;
    /**
     * Transform Vue SFCs into custom elements.
     * - `true`: all `*.vue` imports are converted into custom elements
     * - `string | RegExp`: matched files are converted into custom elements
     *
     * @default /\.ce\.vue$/
     */
    customElement?: boolean | string | RegExp | (string | RegExp)[];
    /**
     * Enable Vue reactivity transform (experimental).
     * https://github.com/vuejs/core/tree/master/packages/reactivity-transform
     * - `true`: transform will be enabled for all vue,js(x),ts(x) files except
     *           those inside node_modules
     * - `string | RegExp`: apply to vue + only matched files (will include
     *                      node_modules, so specify directories in necessary)
     * - `false`: disable in all cases
     *
     * @default false
     */
    reactivityTransform?: boolean | string | RegExp | (string | RegExp)[];
    /**
     * Use custom compiler-sfc instance. Can be used to force a specific version.
     */
    compiler?: typeof _compiler;
}

发现option中是有自定义compiler-sfc的参数,于是就得到以下方案:

// vite.config.ts
import * as compiler from '@vue/compiler-sfc'
export default defineConfig({
// ...
plugins: [
    isVue2
      ? createVuePlugin()
      : vue3({
          compiler: compiler
        })
  ]
})

main.ts

main.ts需要判断isVue2后,区分vue2和vue3的依赖

import { isVue2 } from 'vue-demi'
import { createApp } from 'vue3'
import Vue2 from 'vue2'
import './style.css'
import App from './App.vue'
if (isVue2) {
  const app = new Vue2({
    render: (h) => h(App)
  })
  app.$mount('#app')
} else {
  const app = createApp(App)
  app.mount('#app')
}

postinstall

这里是模仿vue-demi的原理,在安装时利用postinstall钩子执行node脚本,复制lib中的v2/v3目录,具体可直接看文章最后的项目链接;这里有一个地方要注意,由于我是使用vite + ts 构建的项目,package.json中的"type": "module"需要我把所有js改成mjs文件,这个时候,其他项目安装这个项目时,会找不到 __dirname,因此utils.mjs加了以下逻辑。

import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

vue2.7 + vue3 + vite + vue-demi + yarn workspaces

以vue2.7为主包开发时,没办法像vue2.6可以在一个package.json项目下调试和打包,主要是因为vue2.7的代码方式已经是monorepo项目,因此在安装vue2.7的时候,会重新下载@vue/compuler-sfc的2.7.x版本。

所以没办法直接使用@vue/compiler-sfc 包作为vue3的compiler;

那么我们就要换一个思路,做node_modules隔离,而node_modules隔离的方案现在主流的就是yarn workspaces、lerna和pnpm,这里我就以yarn workspaces来简单讲一下思路;

(ps: 该方式我并没有上传到github)

开启yarn workspaces之后,新建packages文件夹

然后再packages下分别新建v2和v3目录,这两个目录存放对应vue2和vue3的package.json和vite.config.ts

// v2/package.json
"scripts": {
    "dev": "vite",
    "build": "rimraf lib/v2 && vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
},
"devDependencies": {
    "@vitejs/plugin-vue2": "^2.7.9",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "2.7.9",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.7.9",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.7.9",
    "vue3": "npm:vue@^3.2.36"
  }
 // v3/package.json
 "scripts": {
    "dev": "vite",
    "build": "rimraf lib/v3 && vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
},
"devDependencies": {
    "@vitejs/plugin-vue": "^3.0.3",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "3.2.26",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.6.14",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.6.14",
    "vue3": "npm:vue@^3.2.26"
  }

vite.config.ts的区别主要是 rollupOptions.output.dir,和对应的plugin,然后alias不需要再指定vue路径,main.ts也不需要区分vue2和vue3的依赖;

// v2/vite.config.ts
import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
// or import vue2 from '@vitejs/plugin-vue2'
import path from 'path'
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  // ...
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  },
  build: {
    // ...
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: resolve('../../lib/v2'), // 区别在这
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [createVuePlugin()] // or vue2()
})
// v3/vite.config.ts
import { defineConfig } from 'vite'
import vue3 from '@vitejs/plugin-vue'
import path from 'path'
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  // ...
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  },
  build: {
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: resolve('../../lib/v3'), // 区别在这
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [vue3()]
})

main.ts

// main.ts
import { createApp } from 'vue-demi'
import './style.css'
const app = createApp(App)
app.mount('#app')

整体目录结构如下,最后通过node脚本去同时构建v2和v3即可。

目前没找到vue3为主包的开发方式

文章看到这里,大概能知道整个方案其实是基于vue-demi处理composition-api和使用vue3的自定义compiler处理分别打包vue2、vue3;而vite-plugin-vue2是没有对应自定义compiler的options,并且在vue3为主包的情况下,会报vue-template-compiler与vue版本不一致的错误;而@vitejs/plugin-vue2存在跟vue3冲突的情况;

目前如果要基于vue3为主包的方式开发,我想到如下2个思路,待后续有时间再去验证:

  • vite-plugin-vue2增加自定义compiler选项
  • 开发rollup插件,支持修改vue-template-compiler在读取require(vue)时,重定向到"vue2": "npm:vue@2.6.14"对应的路径

注意点

1、@vue/composition-api重复引用问题

由于vue-demi在v2.6的场景下,会自动install @vue/composition-api,,如果项目自身也在需要在入口时注册@vue/composition-api,会出现多次注册@vue/composition-api实例的情况,导致出setup相关的报错,这时需要在项目的alias加上以下代码:

alias: {
   '@vue/compostion-api': resolve('./node_modules/@vue/composition-api')
},

2、由于要兼容vue2,vue3的 setup sfc语法糖不兼容

这一点无法解决,写组件template的时候,还是只能用vue2的template写法,包括template还是需要有唯一的跟节点;

最后

写到最后,其实我发现去写兼容vue2和vue3的template代码,并不能完全解决vue2到vue3过渡的问题。希望vue3社区以后越来越完善~

贴上项目地址(vue2.6 + vue3 + vite + vue-demi):vue-demi-sfc-component

以上就是vue demi支持sfc方式的vue2vue3通用库开发详解的详细内容,更多关于vue demi支持sfc通用库的资料请关注我们其它相关文章!

(0)

相关推荐

  • 前端插件库之vue3使用vue-codemirror插件的步骤和实例

    目录 使用 1.命令行安装 2.在需要的组件中配置 配置说明: 个人代码编辑区Demo 总结 vue-codemirror 基于 CodeMirror ,适用于 Vue 的 Web 代码编辑器. 使用 1.命令行安装 npm install vue-codemirror --save // cnpm install vue-codemirror --save 如果运行官网例子时, 报错: @codemirror/lang-javascript@codemirror/theme-one-dark

  • vue实现codemirror代码编辑器中的SQL代码格式化功能

    vue实现codemirror代码编辑器中的SQL代码格式化功能 1.首先使用npm安装sql-formatter插件 npm install --save sql-formatter 2.然后引入该sql-formatter.js文件 import sqlFormatter from "sql-formatter"; 3.接下来就是针对需要格式化的代码调用该方法就OK啦 /*代码格式化*/ format(){ /*获取文本编辑器内容*/ let sqlContent="&q

  •  用Vue Demi 构建同时兼容Vue2与Vue3组件库

    目录 前言: 一.Vue Demi 中的额外 API 1.isVue2 and isVue3 二.Vue Demi 入门 前言: Vue Demi 是一个很棒的包,具有很多潜力和实用性.我强烈建议在创建下一个 Vue 库时使用它. 根据创建者 Anthony Fu 的说法,Vue Demi 是一个开发实用程序,它允许用户为 Vue 2 和 Vue 3 编写通用的 Vue 库,而无需担心用户安装的版本. 以前,要创建支持两个目标版本的 Vue 库,我们会使用不同的分支来分离对每个版本的支持.对于现

  • CodeMirror实现代码对比功能(插件react vue)

    目录 1.第一步:下载插件 2.vue代码如下: 3.react hooks代码如下: 4.一点心得 要实现一个需求:一个代码编辑器,用户上次写的代码和现在的代码要区分出不同.网上找了几个案例,拿过去一用差点气吐血,报错像雪花一样,浪费时间! 本文中的代码,一键粘贴拿来即用,代码就是我在写这篇文章时写的demo,绝无报错. 1.第一步:下载插件 vue或者react都需要这一步且同样的下载方式. npm install diff-match-patch save npm install code

  • vue codemirror实现在线代码编译器

    前言 如果我们想在Web端实现在线代码编译的效果,那么需要使用组件vue-codemirror,他是将CodeMirror进行了再次封装 支持代码高亮 62种主题颜色,例如monokai等等 支持json, sql, javascript,css,xml, html,yaml, markdown, python编辑模式,默认为 json 支持快速搜索 支持自动补全提示 支持自动匹配括号 环境准备 npm install jshint npm install jsonlint npm instal

  • 在vue项目中使用codemirror插件实现代码编辑器功能

    在vue项目中使用codemirror插件实现代码编辑器功能(代码高亮显示及自动提示),具体内容如下所示: 1.使用npm安装依赖 npm install --save codemirror; 2.在页面中放入如下代码 <template> <textarea ref="mycode" class="codesql" v-model="code" style="height:200px;width:600px;&quo

  • vue demi支持sfc方式的vue2vue3通用库开发详解

    目录 背景 技术要点 vue-demi sfc compiler 实现方式 vue2.6 + vue3 + vite + vue-demi package.json vite.config.ts main.ts postinstall vue2.7 + vue3 + vite + vue-demi + yarn workspaces 目前没找到vue3为主包的开发方式 注意点 1.@vue/composition-api重复引用问题 2.由于要兼容vue2,vue3的 setup sfc语法糖不

  • Vue 短信验证码组件开发详解

    Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件. Vue.js 自身不是一个全能框架--它只聚焦于视图层.因此它非常容易学习,非常容易与其它库或已有项目整合.另一方面,在与相关工具和支持库一起使用时,Vue.js 也能完美地驱动复杂的单页应用. 摘要: 1.该组件基于Vue 2.1.X版本: 1. Vue 组件代码如下: Vue.component('timerBtn

  • vue router导航守卫(router.beforeEach())的使用详解

    导航守卫 导航守卫主要用来通过跳转或取消的方式守卫导航.有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的.(记住参数或查询的改变并不会触发进入/离开的导航守卫.你可以通过观察$route对象来应对这些变化,或使用beforeRouteUpdate的组件内守卫.) 好久没写一些东西了,总是感觉有啥缺少的.~~~~恰好碰到最近在写一个移动端项目,遇到了如何使同一个链接在不同条件下跳转到不同路由组件问题,譬如大家经常看到手机中没登录跳转登录页,登陆后跳转个人信息页等.废话不多说了

  • vue axios数据请求get、post方法及实例详解

    我们常用的有get方法以及post方法,下面简单的介绍一下这两种请求方法 vue中使用axios方法我们先安装axios这个方法 npm install --save axios 安装之后采用按需引入的方法,哪个页面需要请求数据就在哪个页面里引入一下. import axios from 'axios' 引入之后我们就可以进行数据请求了,在methods中创建一个方法 methods:{ getInfo(){ let url = "url" axios.get(url).then((r

  • vue.js的状态管理vuex中store的使用详解

    一.状态管理(vuex)简介 vuex是专为vue.js应用程序开发的状态管理模式.它采用集中存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.vuex也集成刀vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试.状态快照导入导出等高级调试功能. 二.状态管理核心 状态管理有5个核心,分别是state.getter.mutation.action以及module.分别简单的介绍一下它们: 1.state state为

  • Vue优化:常见会导致内存泄漏问题及优化详解

    如果你在用 Vue 开发应用,那么就要当心内存泄漏的问题.这个问题在单页应用 (SPA) 中尤为重要,因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效.因此在vue开发过程中,你需要时刻警惕内存泄漏的问题,这些内存泄漏往往会发生在使用 Vue 之外的其它进行 DOM 操作的三方库时,请确保测试应用的内存泄漏问题并在适当的时机做必要的组件清理. 下面是我开发过程中遇到,并查资料总结的内存泄漏问题,会持续更新中

  • Vue注册模块与登录状态的持久化实现方法详解

    目录 整体框架 1. 前端页面授权 2. 实现注册页面 3. 实现登录状态的持久化 优化前端 整体框架 1. 前端页面授权 当我们登录网站的时候,如果没有登录,强制让用户重定向到 登录界面 在 router 目录下的 index.js 文件下实现. router -> index.js import store from '../store/index' // 把一些额外信息放到一个额外的域里面,meta信息里面存一下是否要授权,如果需要授权而且没有登录,重定向到登录页面,重定向到登录界面. c

  • Vue openLayers实现图层数据切换与加载流程详解

    目录 openlayers介绍 一.实现效果预览 二.代码实现 openlayers介绍 OpenLayers是一个用于开发WebGIS客户端的JavaScript包.OpenLayers 支持的地图来源包括Google Maps.Yahoo. Map.微软Virtual Earth 等,用户还可以用简单的图片地图作为背景图,与其他的图层在OpenLayers 中进行叠加,在这一方面OpenLayers提供了非常多的选择.OpenLayers采用面向对象方式开发. OpenLayers 是一个专

  • IIS7.0 Windows Server 2008 R2 下配置证书服务器和HTTPS方式访问网站的教程图文详解

    配置环境 Windows版本:Windows Server 2008 R2 Enterprise Service Pack 1 系统类型: 64 位操作系统 了解HTTPS 为什么需要 HTTPS ? 在我们浏览网站时,多数网站的URL都是以HTTP开头,HTTP协议我们比较熟悉,信息通过明文传输; 使用HTTP协议有它的优点,它与服务器间传输数据更快速准确; 但是HTTP明显是不安全的,我们也可以注意到,当我们在使用邮件或者是在线支付时,都是使用HTTPS; HTTPS传输数据需要使用证书并对

  • Android style的继承方式 点(.)和parent详解及实例

    Android style的继承方式 点(.)和parent详解及实例 一.概述 通过继承机制,可以利用已有的style来定义新的style.所定义的新的style型不仅拥有新定义的item,而且还同时拥有旧的item.我们称已存在的用来派生新的style为父style.由新定义的style,又称为子style.    比如: <style name="pickprof_guide_text"> <item name="android:textSize&qu

随机推荐