Android AGP 升级踩坑经历(4.1.1升级到7.4.2)

一、背景

目前我司各App项目Gradle配置和kotlin配置如下:

  • gradle版本:6.5
  • gradle plugin版本:4.1.1
  • kotlin版本:1.5.10
  • kotlin协程版本:1.5.0
  • asm从5.0升级到7.0(编译插件使用)

但是官方已经迭代到7.5,由于未来需要支持升级google admob sdk到最新版,因此需要将AGP升级到最新版本,所以我们本次的升级目标是:7.5

二、AGP,KGP,Gradle,Kotlin版本对应关系

1、AGP和Gradle对应关系:我们配置Gradle版本7.5,AGP版本7.4

2、Kotlin插件版本和Gradle对应关系:我们选择 Kotlin 插件版本为1.6.21 (官网:kotlinlang.org/docs/gradle...)

3、Kotlin版本与Gradle的对应关系:我们选择kotlin版本为1.6.21(官网:docs.gradle.org/current/use...

4、kotlin版本与kotlin协程库对应版本

因此我们最终升级配置如下:

java 复制代码
//AGP版本

classpath 'com.android.tools.build:gradle:7.4.2'

distributionUrl = https://services.gradle.org/distributions/gradle-7.5-all.zip

//kotlin插件版本

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"

//kotlin版本

ext.kotlin_version = '1.6.21'

//kotlin协程版本

ext.kotlin_coroutines = '1.6.0'

三、开始编译改造

1、注释掉所有自定义插件进行编译, 出现错误:

A problem occurred configuring root project 'SeeyouClient'.

Could not resolve all dependencies for configuration ':classpath'.

> Using insecure protocols with repositories, without explicit opt-in, is unsupported. Switch Maven repository 'maven(maven.meiyou.com/repository/...)' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. See docs.gradle.org/7.5/dsl/org... for more details.

解决办法:将mvaen依赖的http:// 都改为 https://

2、出现错误:includeCompileClasspath找不到

A problem occurred evaluating project ':app'.

Could not set unknown property 'includeCompileClasspath' for AnnotationProcessorOptions <math xmlns="http://www.w3.org/1998/Math/MathML"> A g p D e c o r a t e d D e c o r a t e d c l a s s N a m e s = [ ] , a r g u m e n t s = , c o m p i l e r A r g u m e n t P r o v i d e r s = [ ] o f t y p e c o m . a n d r o i d . b u i l d . g r a d l e . i n t e r n a l . d s l . A n n o t a t i o n P r o c e s s o r O p t i o n s AgpDecorated_Decorated{classNames=[], arguments={}, compilerArgumentProviders=[]} of type com.android.build.gradle.internal.dsl.AnnotationProcessorOptions </math>AgpDecoratedDecoratedclassNames=[],arguments=,compilerArgumentProviders=[]oftypecom.android.build.gradle.internal.dsl.AnnotationProcessorOptionsAgpDecorated.

解决办法:注释掉就可以。

3、The option 'android.enableBuildCache' is deprecated

Failed to apply plugin 'com.android.internal.application'.

> com.android.builder.errors.EvalIssueException: The option 'android.enableBuildCache' is deprecated.

The current default is 'false'.

It was removed in version 7.0 of the Android Gradle plugin.

The Android-specific build caches were superseded by the Gradle build cache (docs.gradle.org/current/use...).

解决办法:

  • 移除android.enableBuildCache

4、错误如下

Could not find method compile() for arguments [directory 'libs'] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

解决办法:

将compile改为 implementation

将testCompile改为testImplementation

将provided改为compileOnly

5、错误如下

WARNING:DSL element 'dexOptions' is obsolete and should be removed.

解决办法:移除如下配置

dexOptions {

jumboMode = true

javaMaxHeapSize "4g"

maxProcessCount 8

incremental true

threadCount = 8

}

6、错误如下

Task :app:compileZroTestDebugRenderscript FAILED

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:compileZroTestDebugRenderscript'.

Could not resolve all files for configuration ':app:zroTestDebugCompileClasspath'.

> Failed to transform dokitx-3.5.0.1.aar (io.github.didi.dokit:dokitx:3.5.0.1) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download dokitx-3.5.0.1.aar (io.github.didi.dokit:dokitx:3.5.0.1): Skipped due to earlier error

> Failed to transform facebook-applinks-14.1.0.aar (com.facebook.android:facebook-applinks:14.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download facebook-applinks-14.1.0.aar (com.facebook.android:facebook-applinks:14.1.0): Skipped due to earlier error

> Failed to transform facebook-login-14.1.0.aar (com.facebook.android:facebook-login:14.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download facebook-login-14.1.0.aar (com.facebook.android:facebook-login:14.1.0): Skipped due to earlier error

> Failed to transform lottie-3.4.0.aar (com.airbnb.android:lottie:3.4.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download lottie-3.4.0.aar (com.airbnb.android:lottie:3.4.0): Skipped due to earlier error

> Failed to transform adrevenue-6.4.3.aar (com.appsflyer:adrevenue:6.4.3) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download adrevenue-6.4.3.aar (com.appsflyer:adrevenue:6.4.3): Skipped due to earlier error

> Failed to transform facebook-common-14.1.0.aar (com.facebook.android:facebook-common:14.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download facebook-common-14.1.0.aar (com.facebook.android:facebook-common:14.1.0): Skipped due to earlier error

> Failed to transform facebook-core-14.1.0.aar (com.facebook.android:facebook-core:14.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download facebook-core-14.1.0.aar (com.facebook.android:facebook-core:14.1.0): Skipped due to earlier error

> Failed to transform audience-network-sdk-6.16.0.aar (com.facebook.android:audience-network-sdk:6.16.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download audience-network-sdk-6.16.0.aar (com.facebook.android:audience-network-sdk:6.16.0): Skipped due to earlier error

> Failed to transform facebook-bolts-14.1.0.aar (com.facebook.android:facebook-bolts:14.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download facebook-bolts-14.1.0.aar (com.facebook.android:facebook-bolts:14.1.0): Skipped due to earlier error

> Failed to transform zxing-lite-2.1.1.aar (com.github.jenly1314:zxing-lite:2.1.1) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download zxing-lite-2.1.1.aar (com.github.jenly1314:zxing-lite:2.1.1): Skipped due to earlier error

> Failed to transform mmkv-1.2.14.aar (com.tencent:mmkv:1.2.14) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download mmkv-1.2.14.aar (com.tencent:mmkv:1.2.14): Skipped due to earlier error

> Failed to transform af-android-sdk-6.4.0.aar (com.appsflyer:af-android-sdk:6.4.0) to match attributes {artifactType=android-renderscript, org.gradle.status=release}.

> Could not download af-android-sdk-6.4.0.aar (com.appsflyer:af-android-sdk:6.4.0): Skipped due to earlier error

> Failed to transform af-android-sdk-6.4.0.aar (com.appsflyer:af-android-sdk:6.4.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download af-android-sdk-6.4.0.aar (com.appsflyer:af-android-sdk:6.4.0): Skipped due to earlier error

> Failed to transform libpag-4.1.12.aar (com.tencent.tav:libpag:4.1.12) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download libpag-4.1.12.aar (com.tencent.tav:libpag:4.1.12): Skipped due to earlier error

> Failed to transform stetho-okhttp3-1.5.1.aar (com.facebook.stetho:stetho-okhttp3:1.5.1) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download stetho-okhttp3-1.5.1.aar (com.facebook.stetho:stetho-okhttp3:1.5.1): Skipped due to earlier error

> Failed to transform stetho-1.5.1.aar (com.facebook.stetho:stetho:1.5.1) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download stetho-1.5.1.aar (com.facebook.stetho:stetho:1.5.1): Skipped due to earlier error

> Failed to transform roundedimageview-2.3.0.aar (com.makeramen:roundedimageview:2.3.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download roundedimageview-2.3.0.aar (com.makeramen:roundedimageview:2.3.0): Skipped due to earlier error

> Failed to transform keyboardvisibilityevent-2.0.1.aar (net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:2.0.1) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download keyboardvisibilityevent-2.0.1.aar (net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:2.0.1): Skipped due to earlier error

> Failed to transform rxandroid-2.1.0.aar (io.reactivex.rxjava2:rxandroid:2.1.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download rxandroid-2.1.0.aar (io.reactivex.rxjava2:rxandroid:2.1.0): Skipped due to earlier error

> Failed to transform crashreport-3.3.9.aar (com.tencent.bugly:crashreport:3.3.9) to match attributes {artifactType=android-renderscript, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download crashreport-3.3.9.aar (com.tencent.bugly:crashreport:3.3.9): Skipped due to earlier error

> Failed to transform nativecrashreport-3.9.0.aar (com.tencent.bugly:nativecrashreport:3.9.0) to match attributes {artifactType=android-renderscript, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.

> Could not download nativecrashreport-3.9.0.aar (com.tencent.bugly:nativecrashreport:3.9.0): Skipped due to earlier error

解决办法:移除support依赖,直接使用android.enableJetifier=true,然后报错的support包代码解决掉;或者把support依赖移除掉。

至此,编译成功。

接下来引入自定义的三大插件:Dilution,Anna,Aspectj

7、错误如下

time.jar

Dilutions jarName:c251730cc767b2c9ce491bd6e41acea1ceb9d5df isExcludeJar:false

==>input.jarInputs file.absolutePath:/Users/ice/.gradle/caches/transforms-3/c4c6f5b862c7c746dc98bab0d039aa81/transformed/jetified-kotlin-stdlib-jdk8-1.6.21.jar

java.lang.NullPointerException

at com.meetyou.dilutions.BlackhandClassVisitor.visit(BlackhandClassVisitor.java:51)

at org.objectweb.asm.ClassReader.accept(ClassReader.java:569)

at org.objectweb.asm.ClassReader.accept(ClassReader.java:424)

at org.objectweb.asm.ClassReader <math xmlns="http://www.w3.org/1998/Math/MathML"> a c c e p t . c a l l ( U n k n o w n S o u r c e ) a t c o m . m e e t y o u . d i l u t i o n s . p l u g i n . P l u g i n I m p l accept.call(Unknown Source) at com.meetyou.dilutions.plugin.PluginImpl </math>accept.call(UnknownSource) atcom.meetyou.dilutions.plugin.PluginImpl_transform_closure1$_closure3.doCall(PluginImpl.groovy:277)

at jdk.internal.reflect.GeneratedMethodAccessor438.invoke(Unknown Source)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.base/java.lang.reflect.Method.invoke(Method.java:566)

at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)

at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)

at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274)

at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035)

at groovy.lang.Closure.call(Closure.java:412)

at groovy.lang.Closure.call(Closure.java:428)

at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMet

解决办法:在适配AGP 4.1.1的时候dilution插件开发了 exclude jar的功能,因此将jetfied-kotlin-stdlib-jdk8加入exclude,如下:

apply plugin: 'dilutions'

dilutionConfig{

excludeJar= "didichuxing.doraemonkit," +

"org.jetbrains.kotlinx," +

"org.jetbrains.kotlin,"

"android.local.jars,"+

"com.didichuxing.doraemonkit,"+

"io.ktor,"+"jetified-kotlin-stdlib-jdk8"

}

8、错误如下:

Dilution插件依然报错,需要直接适配7.4.2,完成打包后编译报错如下

Caused by: org.gradle.api.plugins.UnknownPluginException: Plugin with id 'maven' not found.

原因:7.x废弃了maven,需要改为maven-pulish,但用法和之前的不一样了,详见:

hao88.cloud/2022/01/06/...

我们将mvn.gradle改造成如下:

java 复制代码
apply plugin: 'maven-publish'
project.group = GROUP_ID
project.version = DILUTION_PLUGIN_VERSION
def typePattern = ~"SNAPSHOT$"
def depolyTypeUrl
if(!typePattern.matcher(DILUTION_PLUGIN_VERSION)){
    println "warn ! find release version! will deploy release aar! version is $DILUTION_PLUGIN_VERSION  "
    depolyTypeUrl= deployUrl.replace("snapshots","releases")
    println "depolyTypeUrl DilutionsPlugin : $depolyTypeUrl"
}else {
    depolyTypeUrl = deployUrl
}

//将源码打包
task androidSourcesJar(type: Jar) {
    archiveClassifier.set('sources')
    //from android.sourceSets.main.java.srcDirs
    from sourceSets.main.allSource
}
publishing {
    publications {
        java(MavenPublication) {
            //相应变体
            //from components.debug
            // 定义发布属性,一般为模块包名
            groupId GROUP_ID
            artifactId POM_ARTIFACT_ID
            version DILUTION_PLUGIN_VERSION             // Your package version
            from components.java
            artifact(androidSourcesJar)//将源码打包进aar,如果不需要可以去掉

        }
    }
    //设置存储库信息
    repositories {
        maven {
            url = depolyTypeUrl
            //allowInsecureProtocol = true//允许非https链接
            credentials {
                username deployUserName
                password deployPassword
            }
        }
    }
}

并在settings.gradle加入如下代码

java 复制代码
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url deployReleaseUrl
       
            //允许非https链接
            //allowInsecureProtocol = true
            //当maven开启严格验证的时候需要填写用户名和密码
            credentials {
                username deployUserName
                password deployPassword
            }
        }
    }
}

然后使用./gradlew clean :dilutionsPlugin:publish进行发布;

9、错误如下:

Configuration on demand is an incubating feature.

doPackage value:False

FAILURE: Build completed with 2 failures.

  • Where:

    Build file '/Users/ice/MeiyouCode/PeriodProjectIntl/SeeyouClient/app/build.gradle' line: 15

  • What went wrong:

    A problem occurred evaluating project ':app'.

Plugin with id 'dilutions' not found.

意思是找不到插件id了,看起来似乎是maven-publish使用问题,对比了一下旧版本,应该是因为没打包source导致识别不到id文件,于是完善第八步的打包脚本;

打包aar和jar的方法可以参考如下,以上代码是打包jar的

blog.csdn.net/ljp345775/a...

还有一个问题就是默认插件获取不到配置,代码如下:

java 复制代码
void apply(Project project) {  
project.extensions.create("dilutionConfig", DilutionConfig.class)  
mDilutionConfig =(DilutionConfig) project.property("dilutionConfig")  
println("mDilutionConfig内容:"+mDilutionConfig.toString()) def android = project.extensions.getByType(AppExtension);  
android.registerTransform(this)  
}

应该改为:

scss 复制代码
void apply(Project project) {
    project.extensions.create("dilutionConfig", DilutionConfig.class)
    //def androidComponents = project.extensions.getByType(AndroidComponentsExtension.class)
    project.afterEvaluate {
        mDilutionConfig =(DilutionConfig) project.property("dilutionConfig")
        println("mDilutionConfig内容:"+mDilutionConfig.toString())
    }
    def android = project.extensions.getByType(AppExtension);
    android.registerTransform(this)
}

10、错误如下:

The Transform API uses incremental APIs deprecated since Gradle 7.5. Please add
android.experimental.legacyTransform.forceNonIncremental=true to
gradle.properties to fix this issue. Note that this will run transforms

non-incrementally and may have a build performance impact.

在gradle.properties加入:android.experimental.legacyTransform.forceNonIncremental=true

11、错误如下:

WARNING:API 'android.registerTransform' is obsolete.

It will be removed in version 8.0 of the Android Gradle plugin.

The Transform API is removed to improve build performance. Projects that use the

Transform API force the Android Gradle plugin to use a less optimized flow for the

build that can result in large regressions in build times. It's also difficult to

use the Transform API and combine it with other Gradle features; the replacement

APIs aim to make it easier to extend the build without introducing performance or

correctness issues.

There is no single replacement for the Transform API---there are new, targeted

APIs for each use case. All the replacement APIs are in the
androidComponents {} block.

APG 8.0以后会直接废弃registerTrasnForm,应该使用androidComponents来替代;

至此Dilution插件编译成功,经过测试功能正常;接下来开启Aspectj插件进行编译

12、错误如下:

  • Where:

    Build file '/Users/ice/MeiyouCode/PeriodProjectIntl/SeeyouClient/app/build.gradle' line: 26

  • What went wrong:

    A problem occurred evaluating project ':app'.

Failed to apply plugin 'android-aspectjx'.

> No such property: FD_INTERMEDIATES for class: com.android.builder.model.AndroidProject

说明当前官方aspect插件不支持7.4.2,我们最后找到了一个替代方案:

github.com/wurensen/gr...

apply plugin: 'android-aspectjx'

改为:

apply plugin: 'io.github.wurensen.android-aspectjx'

classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'

改为

classpath 'io.github.wurensen:gradle-android-plugin-aspectjx:3.3.2'

至此 aspect也编译成功;app可正常运行;

13、第三方SDK插件

apply plugin: 'com.google.gms.google-services'

apply plugin: 'com.google.firebase.crashlytics'

apply plugin: 'com.google.firebase.firebase-perf'

需要测试一下功能是否正常,就可以;

至此整体升级验证成功

14、业务模块要怎么做?

开启工程依赖,将agp和kotlin升级到对应版本,然后重新编译打包,验证功能正常就可以了;

尝试将admob sdk升级到最新版本看看是否成功;

15、模块的maven-publish发布aar怎么写?将mvn脚本改为如下:

scss 复制代码
apply plugin: 'maven-publish'
project.group = GROUP_ID
project.version = ADSDK_VERSION
def typePattern = ~"SNAPSHOT$"
def depolyTypeUrl
if(!typePattern.matcher(ADSDK_VERSION)){
    println "warn ! find release version! will deploy release aar! version is $ADSDK_VERSION  "
    depolyTypeUrl= deployUrl.replace("snapshots","releases")
    println "depolyTypeUrl ADSDK : $depolyTypeUrl"
}else {
    depolyTypeUrl = deployUrl
}
//生成文档注释
/*task androidJavadocs(type: Javadoc) {
    failOnError = false
    source = android.sourceSets.main.java.srcDirs
    ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
    classpath += files(ext.androidJar)
}
//将文档打包成jar
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
    archiveClassifier.set('javadoc')
    from androidJavadocs.destinationDir
}*/
//将源码打包
task androidSourcesJar(type: Jar) {
    archiveClassifier.set('sources')
    from android.sourceSets.main.java.srcDirs
    //from sourceSets.main.allSource
}
publishing {
    publications {

        release(MavenPublication) {
            artifact androidSourcesJar // 增加上传源码的 task
            afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
            groupId GROUP_ID
            artifactId POM_ARTIFACT_ID
            version ADSDK_VERSION

            pom.withXml{
                def dependenciesNode = asNode().appendNode("dependencies")
                configurations.implementation.allDependencies.forEach(){
                    Dependency dependency ->
                        if (dependency.version != "unspecified" && dependency.name != "unspecified"){
                            def dependencyNode = dependenciesNode.appendNode('dependency')
                            dependencyNode.appendNode('groupId', dependency.group)
                            dependencyNode.appendNode('artifactId', dependency.name)
                            dependencyNode.appendNode('version', dependency.version)
                        }
                }
                configurations.api.allDependencies.forEach(){
                    Dependency dependency ->
                        if (dependency.version != "unspecified" && dependency.name != "unspecified"){
                            def dependencyNode = dependenciesNode.appendNode('dependency')
                            dependencyNode.appendNode('groupId', dependency.group)
                            dependencyNode.appendNode('artifactId', dependency.name)
                            dependencyNode.appendNode('version', dependency.version)
                        }
                }
            }
        }
    }
    //设置存储库信息
    repositories {
        maven {
            url = depolyTypeUrl
            //allowInsecureProtocol = true//允许非https链接
            credentials {
                username deployUserName
                password deployPassword
            }
        }
    }
}

然后使用./gradlew clean :xxxx:publish发布即可;

至此总体升级验证成功,接下来就是各个模块的编译改造。

参考文章:

juejin.cn/post/701614...

blog.csdn.net/weixin_4180...

blog.csdn.net/ljp345775/a...

juejin.cn/post/729985...

developer.android.com/studio/rele...

相关推荐
正小安1 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光3 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   3 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   3 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web3 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常3 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇4 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr4 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho5 小时前
【TypeScript】知识点梳理(三)
前端·typescript